From 775a5042a739d944d9d58a6c465eab97b37f2cd1 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Mon, 25 Sep 2023 20:54:53 -0700 Subject: [PATCH 1/7] Start skeletal version of changes --- .../fasterxml/jackson/core/JsonFactory.java | 4 +- .../fasterxml/jackson/core/TSFBuilder.java | 7 ++-- .../jackson/core/util/BufferRecycler.java | 8 ++-- .../jackson/core/util/BufferRecyclerPool.java | 41 +++++++++++-------- .../core/util/JsonBufferRecyclerPools.java | 15 +++++++ .../core/io/BufferRecyclerPoolTest.java | 5 ++- 6 files changed, 52 insertions(+), 28 deletions(-) create mode 100644 src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 8b14ef3e1c..91617f734a 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -262,7 +262,7 @@ public static int collectDefaults() { /** * @since 2.16 */ - protected BufferRecyclerPool _bufferRecyclerPool; + protected BufferRecyclerPool _bufferRecyclerPool; /** * Object that implements conversion functionality between @@ -2159,7 +2159,7 @@ public BufferRecycler _getBufferRecycler() * * @since 2.16 */ - public BufferRecyclerPool _getBufferRecyclerPool() { + public BufferRecyclerPool _getBufferRecyclerPool() { // 23-Apr-2015, tatu: Let's allow disabling of buffer recycling // scheme, for cases where it is considered harmful (possibly // on Android, for example) diff --git a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java index 8ee09de195..6e4238cd69 100644 --- a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java +++ b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java @@ -8,6 +8,7 @@ import com.fasterxml.jackson.core.io.OutputDecorator; import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.json.JsonWriteFeature; +import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.BufferRecyclerPool; import com.fasterxml.jackson.core.util.JsonGeneratorDecorator; @@ -74,7 +75,7 @@ public abstract class TSFBuilder _bufferRecyclerPool; /** * Optional helper object that may decorate input sources, to do @@ -169,7 +170,7 @@ protected static List _copy(List src) { public int streamReadFeatures() { return _streamReadFeatures; } public int streamWriteFeatures() { return _streamWriteFeatures; } - public BufferRecyclerPool bufferRecyclerPool() { + public BufferRecyclerPool bufferRecyclerPool() { return _bufferRecyclerPool; } @@ -321,7 +322,7 @@ public B configure(JsonWriteFeature f, boolean state) { * * @since 2.16 */ - public B bufferRecyclerPool(BufferRecyclerPool p) { + public B bufferRecyclerPool(BufferRecyclerPool p) { _bufferRecyclerPool = Objects.requireNonNull(p); return _this(); } diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java index 136d9498df..3fa35f1273 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java @@ -18,6 +18,7 @@ * Rewritten in 2.16 to work with {@link BufferRecyclerPool} abstraction. */ public class BufferRecycler + implements BufferRecyclerPool.WithPool { /** * Buffer used for reading byte-based input. @@ -86,7 +87,7 @@ public class BufferRecycler // Note: changed from simple array in 2.10: protected final AtomicReferenceArray _charBuffers; - private BufferRecyclerPool _pool; + private BufferRecyclerPool _pool; /* /********************************************************** @@ -202,7 +203,8 @@ protected int charBufferLength(int ix) { * * @since 2.16 */ - BufferRecycler withPool(BufferRecyclerPool pool) { + @Override + public BufferRecycler withPool(BufferRecyclerPool pool) { if (this._pool != null) { throw new IllegalStateException("BufferRecycler already linked to pool: "+pool); } @@ -220,7 +222,7 @@ BufferRecycler withPool(BufferRecyclerPool pool) { */ public void release() { if (_pool != null) { - BufferRecyclerPool tmpPool = _pool; + BufferRecyclerPool tmpPool = _pool; // nullify the reference to the pool in order to avoid the risk of releasing // the same BufferRecycler more than once, thus compromising the pool integrity _pool = null; diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index deeae7074c..b640e4edf6 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -35,8 +35,12 @@ * * @since 2.16 */ -public interface BufferRecyclerPool extends Serializable +public interface BufferRecyclerPool

> extends Serializable { + public interface WithPool

> { + P withPool(BufferRecyclerPool

pool); + } + /** * Method called to acquire a {@link BufferRecycler} from this pool * AND make sure it is linked back to this @@ -51,7 +55,7 @@ public interface BufferRecyclerPool extends Serializable * @return {@link BufferRecycler} for caller to use; caller expected * to call {@link #releaseBufferRecycler} after it is done using recycler. */ - default BufferRecycler acquireAndLinkBufferRecycler() { + default P acquireAndLinkBufferRecycler() { return acquireBufferRecycler().withPool(this); } @@ -59,7 +63,7 @@ default BufferRecycler acquireAndLinkBufferRecycler() { * Method for sub-classes to implement for actual acquire logic; called * by {@link #acquireAndLinkBufferRecycler()}. */ - BufferRecycler acquireBufferRecycler(); + P acquireBufferRecycler(); /** * Method that should be called when previously acquired (see {@link #acquireAndLinkBufferRecycler}) @@ -68,14 +72,14 @@ default BufferRecycler acquireAndLinkBufferRecycler() { * * @param recycler */ - void releaseBufferRecycler(BufferRecycler recycler); + void releaseBufferRecycler(P recycler); /** * @return the default {@link BufferRecyclerPool} implementation * which is the thread local based one: * basically alias to {@link #threadLocalPool()}). */ - static BufferRecyclerPool defaultPool() { + static BufferRecyclerPool defaultPool() { return threadLocalPool(); } @@ -83,7 +87,7 @@ static BufferRecyclerPool defaultPool() { * @return Globally shared instance of {@link ThreadLocalPool}; same as calling * {@link ThreadLocalPool#shared()}. */ - static BufferRecyclerPool threadLocalPool() { + static BufferRecyclerPool threadLocalPool() { return ThreadLocalPool.shared(); } @@ -91,7 +95,7 @@ static BufferRecyclerPool threadLocalPool() { * @return Globally shared instance of {@link NonRecyclingPool}; same as calling * {@link NonRecyclingPool#shared()}. */ - static BufferRecyclerPool nonRecyclingPool() { + static BufferRecyclerPool nonRecyclingPool() { return NonRecyclingPool.shared(); } @@ -112,11 +116,11 @@ static BufferRecyclerPool nonRecyclingPool() { * Android), or on platforms where {@link java.lang.Thread}s are not * long-living or reused (like Project Loom). */ - class ThreadLocalPool implements BufferRecyclerPool + class ThreadLocalPool implements BufferRecyclerPool { private static final long serialVersionUID = 1L; - private static final BufferRecyclerPool GLOBAL = new ThreadLocalPool(); + private static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); /** * Accessor for the global, shared instance of {@link ThreadLocal}-based @@ -125,7 +129,7 @@ class ThreadLocalPool implements BufferRecyclerPool * * @return Shared pool instance */ - public static BufferRecyclerPool shared() { + public static ThreadLocalPool shared() { return GLOBAL; } @@ -160,11 +164,11 @@ public void releaseBufferRecycler(BufferRecycler recycler) { * {@link BufferRecyclerPool} implementation that does not use * any pool but simply creates new instances when necessary. */ - class NonRecyclingPool implements BufferRecyclerPool + class NonRecyclingPool implements BufferRecyclerPool { private static final long serialVersionUID = 1L; - private static final BufferRecyclerPool GLOBAL = new NonRecyclingPool(); + private static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); // No instances beyond shared one should be constructed private NonRecyclingPool() { } @@ -175,7 +179,7 @@ private NonRecyclingPool() { } * * @return Shared pool instance */ - public static BufferRecyclerPool shared() { + public static NonRecyclingPool shared() { return GLOBAL; } @@ -208,7 +212,8 @@ public void releaseBufferRecycler(BufferRecycler recycler) { * special handling with respect to JDK serialization, to retain * "global" reference distinct from non-shared ones. */ - public abstract static class StatefulImplBase implements BufferRecyclerPool + public abstract static class StatefulImplBase

> + implements BufferRecyclerPool

{ private static final long serialVersionUID = 1L; @@ -227,7 +232,7 @@ protected StatefulImplBase(int serialization) { _serialization = serialization; } - protected Optional _resolveToShared(BufferRecyclerPool shared) { + protected Optional> _resolveToShared(StatefulImplBase

shared) { if (_serialization == SERIALIZATION_SHARED) { return Optional.of(shared); } @@ -241,7 +246,7 @@ protected Optional _resolveToShared(BufferRecyclerPool share *

* Pool is unbounded: see {@link BufferRecyclerPool} what this means. */ - class ConcurrentDequePool extends StatefulImplBase + class ConcurrentDequePool extends StatefulImplBase { private static final long serialVersionUID = 1L; @@ -308,7 +313,7 @@ protected Object readResolve() { * Pool is unbounded: see {@link BufferRecyclerPool} for * details on what this means. */ - class LockFreePool extends StatefulImplBase + class LockFreePool extends StatefulImplBase { private static final long serialVersionUID = 1L; @@ -405,7 +410,7 @@ private static class Node { * {@link BufferRecycler} instances than its size configuration: * the default size is {@link BoundedPool#DEFAULT_CAPACITY}. */ - class BoundedPool extends StatefulImplBase + class BoundedPool extends StatefulImplBase { private static final long serialVersionUID = 1L; diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java new file mode 100644 index 0000000000..1a3094db88 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java @@ -0,0 +1,15 @@ +package com.fasterxml.jackson.core.util; + +import com.fasterxml.jackson.core.JsonFactory; + +/** + * Set of {@link BufferRecyclerPool} implementations to be used by the default + * JSON-backed {@link JsonFactory} for recycling {@link BufferRecycler} + * containers. + * + * @since 2.16 + */ +public final class JsonBufferRecyclerPools +{ + +} diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index 4bdbac5dd3..f86fd6e380 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -37,7 +37,8 @@ public void testPluggingPool() throws Exception { checkBufferRecyclerPoolImpl(new TestPool(), true); } - private void checkBufferRecyclerPoolImpl(BufferRecyclerPool pool, boolean checkPooledResource) throws Exception { + private void checkBufferRecyclerPoolImpl(BufferRecyclerPool pool, + boolean checkPooledResource) throws Exception { JsonFactory jsonFactory = JsonFactory.builder() .bufferRecyclerPool(pool) .build(); @@ -82,7 +83,7 @@ private static class NopOutputStream extends OutputStream { @SuppressWarnings("serial") - class TestPool implements BufferRecyclerPool + class TestPool implements BufferRecyclerPool { private BufferRecycler bufferRecycler; From dec745ac2df1428be662b1626974a93ae50039b8 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Sep 2023 20:18:40 -0700 Subject: [PATCH 2/7] Convert ThreadLocal-based pool to parameterized version --- .../fasterxml/jackson/core/JsonFactory.java | 4 +- .../fasterxml/jackson/core/TSFBuilder.java | 3 +- .../jackson/core/util/BufferRecyclerPool.java | 63 +++++----------- .../core/util/JsonBufferRecyclerPools.java | 15 ---- .../core/util/JsonBufferRecyclers.java | 72 +++++++++++++++++++ .../jackson/core/TestJDKSerializability.java | 3 +- .../core/io/BufferRecyclerPoolTest.java | 3 +- 7 files changed, 96 insertions(+), 67 deletions(-) delete mode 100644 src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java create mode 100644 src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 91617f734a..b9db917cac 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -368,7 +368,7 @@ public static int collectDefaults() { public JsonFactory() { this((ObjectCodec) null); } public JsonFactory(ObjectCodec oc) { - _bufferRecyclerPool = BufferRecyclerPool.defaultPool(); + _bufferRecyclerPool = JsonBufferRecyclers.defaultPool(); _objectCodec = oc; _quoteChar = DEFAULT_QUOTE_CHAR; _streamReadConstraints = StreamReadConstraints.defaults(); @@ -1152,7 +1152,7 @@ public String getRootValueSeparator() { /********************************************************** */ - public JsonFactory setBufferRecyclerPool(BufferRecyclerPool p) { + public JsonFactory setBufferRecyclerPool(BufferRecyclerPool p) { _bufferRecyclerPool = Objects.requireNonNull(p); return this; } diff --git a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java index 6e4238cd69..71e461dc61 100644 --- a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java +++ b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.core.json.JsonWriteFeature; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.BufferRecyclerPool; +import com.fasterxml.jackson.core.util.JsonBufferRecyclers; import com.fasterxml.jackson.core.util.JsonGeneratorDecorator; /** @@ -142,7 +143,7 @@ protected TSFBuilder(JsonFactory base) protected TSFBuilder(int factoryFeatures, int parserFeatures, int generatorFeatures) { - _bufferRecyclerPool = BufferRecyclerPool.defaultPool(); + _bufferRecyclerPool = JsonBufferRecyclers.defaultPool(); _factoryFeatures = factoryFeatures; _streamReadFeatures = parserFeatures; diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index b640e4edf6..8a4f0ac01c 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -18,7 +18,7 @@ * will always simply construct and return new instance when {@code acquireBufferRecycler} * is called * - *

  • {@link ThreadLocalPool} which uses {@link ThreadLocal} to retain at most + *
  • {@link ThreadLocalPoolBase} which uses {@link ThreadLocal} to retain at most * 1 recycler per {@link Thread}. *
  • *
  • {@link BoundedPool} is "bounded pool" and retains at most N recyclers (default value being @@ -33,10 +33,17 @@ *

    * Default implementations are also included as nested classes. * + * @param

    Type of Objects pool recycles + * * @since 2.16 */ public interface BufferRecyclerPool

    > extends Serializable { + /** + * Simple add-on interface that poolable entities must implement. + * + * @param

    Self type + */ public interface WithPool

    > { P withPool(BufferRecyclerPool

    pool); } @@ -74,23 +81,6 @@ default P acquireAndLinkBufferRecycler() { */ void releaseBufferRecycler(P recycler); - /** - * @return the default {@link BufferRecyclerPool} implementation - * which is the thread local based one: - * basically alias to {@link #threadLocalPool()}). - */ - static BufferRecyclerPool defaultPool() { - return threadLocalPool(); - } - - /** - * @return Globally shared instance of {@link ThreadLocalPool}; same as calling - * {@link ThreadLocalPool#shared()}. - */ - static BufferRecyclerPool threadLocalPool() { - return ThreadLocalPool.shared(); - } - /** * @return Globally shared instance of {@link NonRecyclingPool}; same as calling * {@link NonRecyclingPool#shared()}. @@ -101,14 +91,14 @@ static BufferRecyclerPool nonRecyclingPool() { /* /********************************************************************** - /* Default BufferRecyclerPool implementations + /* Partial/base BufferRecyclerPool implementations /********************************************************************** */ /** * Default {@link BufferRecyclerPool} implementation that uses - * {@link ThreadLocal} for recycling instances. {@link BufferRecycler} - * instances are stored using {@link java.lang.ref.SoftReference}s so that + * {@link ThreadLocal} for recycling instances. + * Instances are stored using {@link java.lang.ref.SoftReference}s so that * they may be Garbage Collected as needed by JVM. *

    * Note that this implementation may not work well on platforms where @@ -116,48 +106,27 @@ static BufferRecyclerPool nonRecyclingPool() { * Android), or on platforms where {@link java.lang.Thread}s are not * long-living or reused (like Project Loom). */ - class ThreadLocalPool implements BufferRecyclerPool + abstract class ThreadLocalPoolBase

    > implements BufferRecyclerPool

    { private static final long serialVersionUID = 1L; - private static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); - - /** - * Accessor for the global, shared instance of {@link ThreadLocal}-based - * pool: due to its nature it is essentially Singleton as there can only - * be a single recycled {@link BufferRecycler} per thread. - * - * @return Shared pool instance - */ - public static ThreadLocalPool shared() { - return GLOBAL; - } - - // No instances beyond shared one should be constructed - private ThreadLocalPool() { } + protected ThreadLocalPoolBase() { } // // // Actual API implementation @Override - public BufferRecycler acquireAndLinkBufferRecycler() { + public P acquireAndLinkBufferRecycler() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler return acquireBufferRecycler(); } - @SuppressWarnings("deprecation") @Override - public BufferRecycler acquireBufferRecycler() { - return BufferRecyclers.getBufferRecycler(); - } + public abstract P acquireBufferRecycler(); @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releaseBufferRecycler(P recycler) { ; // nothing to do, relies on ThreadLocal } - - // // // JDK serialization support - - protected Object readResolve() { return GLOBAL; } } /** diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java deleted file mode 100644 index 1a3094db88..0000000000 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclerPools.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.fasterxml.jackson.core.util; - -import com.fasterxml.jackson.core.JsonFactory; - -/** - * Set of {@link BufferRecyclerPool} implementations to be used by the default - * JSON-backed {@link JsonFactory} for recycling {@link BufferRecycler} - * containers. - * - * @since 2.16 - */ -public final class JsonBufferRecyclerPools -{ - -} diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java new file mode 100644 index 0000000000..d5a6874440 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -0,0 +1,72 @@ +package com.fasterxml.jackson.core.util; + +import com.fasterxml.jackson.core.JsonFactory; + +/** + * Set of {@link BufferRecyclerPool} implementations to be used by the default + * JSON-backed {@link JsonFactory} for recycling {@link BufferRecycler} + * containers. + * + * @since 2.16 + */ +public final class JsonBufferRecyclers +{ + /** + * @return the default {@link BufferRecyclerPool} implementation + * which is the thread local based one: + * basically alias to {@link #threadLocalPool()}). + */ + public static BufferRecyclerPool defaultPool() { + return threadLocalPool(); + } + /** + * @return Globally shared instance of {@link ThreadLocalPool}; same as calling + * {@link ThreadLocalPool#shared()}. + */ + public static BufferRecyclerPool threadLocalPool() { + return ThreadLocalPool.shared(); + } + + /* + /********************************************************************** + /* Concrete BufferRecyclerPool implementations for recycling BufferRecyclers + /********************************************************************** + */ + + /** + * {@link ThreadLocal}-based {@link BufferRecyclerPool} implemenetation used for + * recycling {@link BufferRecycler} instances: + * see {@link BufferRecyclerPool.ThreadLocalPoolBase} for full explanation + * of functioning. + */ + public static class ThreadLocalPool + extends BufferRecyclerPool.ThreadLocalPoolBase + { + private static final long serialVersionUID = 1L; + + private static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); + + private ThreadLocalPool() { } + + /** + * Accessor for the global, shared instance of this pool type: + * due to its nature it is a Singleton as there can only + * be a single recycled {@link BufferRecycler} per thread. + * + * @return Shared pool instance + */ + public static ThreadLocalPool shared() { + return GLOBAL; + } + + @SuppressWarnings("deprecation") + @Override + public BufferRecycler acquireBufferRecycler() { + return BufferRecyclers.getBufferRecycler(); + } + + // // // JDK serialization support + + protected Object readResolve() { return GLOBAL; } + } +} diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java index d03740d152..e0f44a13f5 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java +++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.core.io.ContentReference; import com.fasterxml.jackson.core.util.BufferRecyclerPool; import com.fasterxml.jackson.core.util.DefaultPrettyPrinter; +import com.fasterxml.jackson.core.util.JsonBufferRecyclers; /** * Unit tests for [core#31] (https://github.com/FasterXML/jackson-core/issues/31) @@ -117,7 +118,7 @@ public void testRecyclerPools() throws Exception // First: shared/global pools that will always remain/become globally // shared instances _testRecyclerPoolGlobal(BufferRecyclerPool.nonRecyclingPool()); - _testRecyclerPoolGlobal(BufferRecyclerPool.threadLocalPool()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.threadLocalPool()); _testRecyclerPoolGlobal(BufferRecyclerPool.ConcurrentDequePool.shared()); _testRecyclerPoolGlobal(BufferRecyclerPool.LockFreePool.shared()); diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index f86fd6e380..65e900a3a8 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.core.json.JsonGeneratorImpl; import com.fasterxml.jackson.core.util.BufferRecycler; import com.fasterxml.jackson.core.util.BufferRecyclerPool; +import com.fasterxml.jackson.core.util.JsonBufferRecyclers; import java.io.IOException; import java.io.OutputStream; @@ -18,7 +19,7 @@ public void testNoOp() throws Exception { } public void testThreadLocal() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.ThreadLocalPool.shared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.threadLocalPool(), true); } public void testLockFree() throws Exception { From b28f9c32356d969f305a2c3095339eb88f998af2 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Sep 2023 20:30:46 -0700 Subject: [PATCH 3/7] Convert second pool implementation (non-recycling) --- .../fasterxml/jackson/core/JsonFactory.java | 2 +- .../jackson/core/util/BufferRecyclerPool.java | 42 +++---------------- .../core/util/JsonBufferRecyclers.java | 42 +++++++++++++++++++ .../jackson/core/TestJDKSerializability.java | 6 +-- .../core/io/BufferRecyclerPoolTest.java | 2 +- 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index b9db917cac..4fd78174f7 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -2164,7 +2164,7 @@ public BufferRecyclerPool _getBufferRecyclerPool() { // scheme, for cases where it is considered harmful (possibly // on Android, for example) if (!Feature.USE_THREAD_LOCAL_FOR_BUFFER_RECYCLING.enabledIn(_factoryFeatures)) { - return BufferRecyclerPool.nonRecyclingPool(); + return JsonBufferRecyclers.nonRecyclingPool(); } return _bufferRecyclerPool; } diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index 8a4f0ac01c..fefad082ed 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -11,10 +11,10 @@ * API for entity that controls creation and possible reuse of {@link BufferRecycler} * instances used for recycling of underlying input/output buffers. *

    - * Different pool implementations use different strategies on retaining + * Also contains partial base implementations for pools that use different strategies on retaining * recyclers for reuse. For example we have: *

      - *
    • {@link NonRecyclingPool} which does not retain any recyclers and + *
    • {@link NonRecyclingPoolBase} which does not retain or recycler anything and * will always simply construct and return new instance when {@code acquireBufferRecycler} * is called *
    • @@ -81,14 +81,6 @@ default P acquireAndLinkBufferRecycler() { */ void releaseBufferRecycler(P recycler); - /** - * @return Globally shared instance of {@link NonRecyclingPool}; same as calling - * {@link NonRecyclingPool#shared()}. - */ - static BufferRecyclerPool nonRecyclingPool() { - return NonRecyclingPool.shared(); - } - /* /********************************************************************** /* Partial/base BufferRecyclerPool implementations @@ -133,47 +125,25 @@ public void releaseBufferRecycler(P recycler) { * {@link BufferRecyclerPool} implementation that does not use * any pool but simply creates new instances when necessary. */ - class NonRecyclingPool implements BufferRecyclerPool + abstract class NonRecyclingPoolBase

      > implements BufferRecyclerPool

      { private static final long serialVersionUID = 1L; - private static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); - - // No instances beyond shared one should be constructed - private NonRecyclingPool() { } - - /** - * Accessor for the shared singleton instance; due to implementation having no state - * this is preferred over creating instances. - * - * @return Shared pool instance - */ - public static NonRecyclingPool shared() { - return GLOBAL; - } - // // // Actual API implementation @Override - public BufferRecycler acquireAndLinkBufferRecycler() { + public P acquireAndLinkBufferRecycler() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler return acquireBufferRecycler(); } @Override - public BufferRecycler acquireBufferRecycler() { - // Could link back to this pool as marker? For now just leave back-ref empty - return new BufferRecycler(); - } + public abstract P acquireBufferRecycler(); @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releaseBufferRecycler(P recycler) { ; // nothing to do, there is no underlying pool } - - // // // JDK serialization support - - protected Object readResolve() { return GLOBAL; } } /** diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java index d5a6874440..4d86e05957 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -27,6 +27,14 @@ public static BufferRecyclerPool threadLocalPool() { return ThreadLocalPool.shared(); } + /** + * @return Globally shared instance of {@link NonRecyclingPool}; same as calling + * {@link NonRecyclingPool#shared()}. + */ + public static BufferRecyclerPool nonRecyclingPool() { + return NonRecyclingPool.shared(); + } + /* /********************************************************************** /* Concrete BufferRecyclerPool implementations for recycling BufferRecyclers @@ -69,4 +77,38 @@ public BufferRecycler acquireBufferRecycler() { protected Object readResolve() { return GLOBAL; } } + + /** + * Dummy {@link BufferRecyclerPool} implementation that does not recycle + * anything but simply creates new instances when asked to acquire items. + */ + public static class NonRecyclingPool + extends BufferRecyclerPool.NonRecyclingPoolBase + { + private static final long serialVersionUID = 1L; + + private static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); + + // No instances beyond shared one should be constructed + private NonRecyclingPool() { } + + @Override + public BufferRecycler acquireBufferRecycler() { + return new BufferRecycler(); + } + + /** + * Accessor for the shared singleton instance; due to implementation having no state + * this is preferred over creating instances. + * + * @return Shared pool instance + */ + public static NonRecyclingPool shared() { + return GLOBAL; + } + + // // // JDK serialization support + + protected Object readResolve() { return GLOBAL; } + } } diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java index e0f44a13f5..47d11be702 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java +++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java @@ -117,7 +117,7 @@ public void testRecyclerPools() throws Exception { // First: shared/global pools that will always remain/become globally // shared instances - _testRecyclerPoolGlobal(BufferRecyclerPool.nonRecyclingPool()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.nonRecyclingPool()); _testRecyclerPoolGlobal(JsonBufferRecyclers.threadLocalPool()); _testRecyclerPoolGlobal(BufferRecyclerPool.ConcurrentDequePool.shared()); @@ -132,7 +132,7 @@ public void testRecyclerPools() throws Exception assertEquals(250, bounded.capacity()); } - private T _testRecyclerPoolGlobal(T pool) throws Exception { + private > T _testRecyclerPoolGlobal(T pool) throws Exception { byte[] stuff = jdkSerialize(pool); T result = jdkDeserialize(stuff); assertNotNull(result); @@ -140,7 +140,7 @@ private T _testRecyclerPoolGlobal(T pool) throws return result; } - private T _testRecyclerPoolNonShared(T pool) throws Exception { + private > T _testRecyclerPoolNonShared(T pool) throws Exception { byte[] stuff = jdkSerialize(pool); T result = jdkDeserialize(stuff); assertNotNull(result); diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index 65e900a3a8..93e3ae47e9 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -15,7 +15,7 @@ public class BufferRecyclerPoolTest extends BaseTest { public void testNoOp() throws Exception { // no-op pool doesn't actually pool anything, so avoid checking it - checkBufferRecyclerPoolImpl(BufferRecyclerPool.NonRecyclingPool.shared(), false); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.nonRecyclingPool(), false); } public void testThreadLocal() throws Exception { From 64276f3fbf3be2c73fc5beae6d22f5192df3c7c2 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Tue, 26 Sep 2023 21:31:05 -0700 Subject: [PATCH 4/7] DequePool converted too --- .../jackson/core/util/BufferRecyclerPool.java | 68 +++----------- .../core/util/JsonBufferRecyclers.java | 94 ++++++++++++++----- .../jackson/core/TestJDKSerializability.java | 4 +- .../core/io/BufferRecyclerPoolTest.java | 2 +- 4 files changed, 86 insertions(+), 82 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index fefad082ed..0f39ebb01c 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -24,7 +24,7 @@ *

    • {@link BoundedPool} is "bounded pool" and retains at most N recyclers (default value being * {@link BoundedPool#DEFAULT_CAPACITY}) at any given time. *
    • - *
    • Two implementations -- {@link ConcurrentDequePool}, {@link LockFreePool} + *
    • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePool} * -- are "unbounded" and retain any number of recyclers released: in practice * it is at most the highest number of concurrently used {@link BufferRecycler}s. *
    • @@ -116,7 +116,7 @@ public P acquireAndLinkBufferRecycler() { public abstract P acquireBufferRecycler(); @Override - public void releaseBufferRecycler(P recycler) { + public void releaseBufferRecycler(P pooled) { ; // nothing to do, relies on ThreadLocal } } @@ -141,7 +141,7 @@ public P acquireAndLinkBufferRecycler() { public abstract P acquireBufferRecycler(); @Override - public void releaseBufferRecycler(P recycler) { + public void releaseBufferRecycler(P pooled) { ; // nothing to do, there is no underlying pool } } @@ -151,14 +151,14 @@ public void releaseBufferRecycler(P recycler) { * special handling with respect to JDK serialization, to retain * "global" reference distinct from non-shared ones. */ - public abstract static class StatefulImplBase

      > + abstract class StatefulImplBase

      > implements BufferRecyclerPool

      { private static final long serialVersionUID = 1L; - protected final static int SERIALIZATION_SHARED = -1; + public final static int SERIALIZATION_SHARED = -1; - protected final static int SERIALIZATION_NON_SHARED = 1; + public final static int SERIALIZATION_NON_SHARED = 1; /** * Value that indicates basic aspects of pool for JDK serialization; @@ -185,66 +185,28 @@ protected Optional> _resolveToShared(StatefulImplBase

      sha *

      * Pool is unbounded: see {@link BufferRecyclerPool} what this means. */ - class ConcurrentDequePool extends StatefulImplBase + abstract class ConcurrentDequePoolBase

      > + extends StatefulImplBase

      { private static final long serialVersionUID = 1L; - private static final ConcurrentDequePool GLOBAL = new ConcurrentDequePool(SERIALIZATION_SHARED); + protected final transient Deque

      pool; - private final transient Deque pool; - - // // // Life-cycle (constructors, factory methods) - - protected ConcurrentDequePool(int serialization) { + protected ConcurrentDequePoolBase(int serialization) { super(serialization); pool = new ConcurrentLinkedDeque<>(); } - /** - * Accessor for getting the globally shared singleton instance. - * Note that if you choose to use this instance, - * pool may be shared by many other {@code JsonFactory} instances. - * - * @return Shared pool instance - */ - public static ConcurrentDequePool shared() { - return GLOBAL; - } - - /** - * Accessor for creating and returning a new, non-shared pool instance. - * - * @return Newly constructed, non-shared pool instance - */ - public static ConcurrentDequePool nonShared() { - return new ConcurrentDequePool(SERIALIZATION_NON_SHARED); - } - // // // Actual API implementation - - @Override - public BufferRecycler acquireBufferRecycler() { - BufferRecycler bufferRecycler = pool.pollFirst(); - if (bufferRecycler == null) { - bufferRecycler = new BufferRecycler(); - } - return bufferRecycler; - } @Override - public void releaseBufferRecycler(BufferRecycler bufferRecycler) { - pool.offerLast(bufferRecycler); - } - - // // // JDK serialization support + public abstract P acquireBufferRecycler(); - /** - * Make sure to re-link to global/shared or non-shared. - */ - protected Object readResolve() { - return _resolveToShared(GLOBAL).orElseGet(() -> nonShared()); + @Override + public void releaseBufferRecycler(P pooled) { + pool.offerLast(pooled); } -} + } /** * {@link BufferRecyclerPool} implementation that uses diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java index 4d86e05957..da8489bb78 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -1,6 +1,9 @@ package com.fasterxml.jackson.core.util; +import java.util.concurrent.ConcurrentLinkedDeque; + import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.util.BufferRecyclerPool.ConcurrentDequePoolBase; /** * Set of {@link BufferRecyclerPool} implementations to be used by the default @@ -20,19 +23,23 @@ public static BufferRecyclerPool defaultPool() { return threadLocalPool(); } /** - * @return Globally shared instance of {@link ThreadLocalPool}; same as calling - * {@link ThreadLocalPool#shared()}. + * Accessor for getting the shared/global {@link ThreadLocalPool} instance + * (due to design only one instance ever needed) + * + * @return Globally shared instance of {@link ThreadLocalPool} */ public static BufferRecyclerPool threadLocalPool() { - return ThreadLocalPool.shared(); + return ThreadLocalPool.GLOBAL; } /** - * @return Globally shared instance of {@link NonRecyclingPool}; same as calling - * {@link NonRecyclingPool#shared()}. + * Accessor for getting the shared/global {@link NonRecyclingPool} instance + * (due to design only one instance ever needed) + * + * @return Globally shared instance of {@link NonRecyclingPool}. */ public static BufferRecyclerPool nonRecyclingPool() { - return NonRecyclingPool.shared(); + return NonRecyclingPool.GLOBAL; } /* @@ -52,20 +59,9 @@ public static class ThreadLocalPool { private static final long serialVersionUID = 1L; - private static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); + protected static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); private ThreadLocalPool() { } - - /** - * Accessor for the global, shared instance of this pool type: - * due to its nature it is a Singleton as there can only - * be a single recycled {@link BufferRecycler} per thread. - * - * @return Shared pool instance - */ - public static ThreadLocalPool shared() { - return GLOBAL; - } @SuppressWarnings("deprecation") @Override @@ -87,28 +83,74 @@ public static class NonRecyclingPool { private static final long serialVersionUID = 1L; - private static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); + protected static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); - // No instances beyond shared one should be constructed - private NonRecyclingPool() { } + protected NonRecyclingPool() { } @Override public BufferRecycler acquireBufferRecycler() { return new BufferRecycler(); } + // // // JDK serialization support + + protected Object readResolve() { return GLOBAL; } + } + + /** + * {@link BufferRecyclerPool} implementation that uses + * {@link ConcurrentLinkedDeque} for recycling instances. + *

      + * Pool is unbounded: see {@link BufferRecyclerPool} what this means. + */ + public static class ConcurrentDequePool extends ConcurrentDequePoolBase + { + private static final long serialVersionUID = 1L; + + private static final ConcurrentDequePool GLOBAL = new ConcurrentDequePool(SERIALIZATION_SHARED); + + // // // Life-cycle (constructors, factory methods) + + protected ConcurrentDequePool(int serialization) { + super(serialization); + } + /** - * Accessor for the shared singleton instance; due to implementation having no state - * this is preferred over creating instances. + * Accessor for getting the globally shared singleton instance. + * Note that if you choose to use this instance, + * pool may be shared by many other {@code JsonFactory} instances. * * @return Shared pool instance */ - public static NonRecyclingPool shared() { + public static ConcurrentDequePool shared() { return GLOBAL; } - + + /** + * Accessor for creating and returning a new, non-shared pool instance. + * + * @return Newly constructed, non-shared pool instance + */ + public static ConcurrentDequePool nonShared() { + return new ConcurrentDequePool(SERIALIZATION_NON_SHARED); + } + + @Override + public BufferRecycler acquireBufferRecycler() { + BufferRecycler bufferRecycler = pool.pollFirst(); + if (bufferRecycler == null) { + bufferRecycler = new BufferRecycler(); + } + return bufferRecycler; + } + // // // JDK serialization support - protected Object readResolve() { return GLOBAL; } + /** + * Make sure to re-link to global/shared or non-shared. + */ + protected Object readResolve() { + return _resolveToShared(GLOBAL).orElseGet(() -> nonShared()); + } } } diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java index 47d11be702..58474a8b36 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java +++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java @@ -120,13 +120,13 @@ public void testRecyclerPools() throws Exception _testRecyclerPoolGlobal(JsonBufferRecyclers.nonRecyclingPool()); _testRecyclerPoolGlobal(JsonBufferRecyclers.threadLocalPool()); - _testRecyclerPoolGlobal(BufferRecyclerPool.ConcurrentDequePool.shared()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.ConcurrentDequePool.shared()); _testRecyclerPoolGlobal(BufferRecyclerPool.LockFreePool.shared()); BufferRecyclerPool.BoundedPool bounded = _testRecyclerPoolGlobal(BufferRecyclerPool.BoundedPool.shared()); assertEquals(BufferRecyclerPool.BoundedPool.DEFAULT_CAPACITY, bounded.capacity()); - _testRecyclerPoolNonShared(BufferRecyclerPool.ConcurrentDequePool.nonShared()); + _testRecyclerPoolNonShared(JsonBufferRecyclers.ConcurrentDequePool.nonShared()); _testRecyclerPoolNonShared(BufferRecyclerPool.LockFreePool.nonShared()); bounded = _testRecyclerPoolNonShared(BufferRecyclerPool.BoundedPool.nonShared(250)); assertEquals(250, bounded.capacity()); diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index 93e3ae47e9..25f68817dd 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -27,7 +27,7 @@ public void testLockFree() throws Exception { } public void testConcurrentDequeue() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.ConcurrentDequePool.nonShared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.ConcurrentDequePool.nonShared(), true); } public void testBounded() throws Exception { From 58edb25619ebcd93696a647146874a6f822a0738 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 27 Sep 2023 20:12:43 -0700 Subject: [PATCH 5/7] Major refactoring/renaming --- .../fasterxml/jackson/core/JsonFactory.java | 2 +- .../jackson/core/util/BufferRecycler.java | 2 +- .../jackson/core/util/BufferRecyclerPool.java | 140 ++++++++---------- .../core/util/JsonBufferRecyclers.java | 104 +++++++++---- .../jackson/core/TestJDKSerializability.java | 8 +- .../core/io/BufferRecyclerPoolTest.java | 10 +- 6 files changed, 149 insertions(+), 117 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 4fd78174f7..2edd6b4090 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java @@ -2150,7 +2150,7 @@ protected JsonGenerator _decorate(JsonGenerator g) { */ public BufferRecycler _getBufferRecycler() { - return _getBufferRecyclerPool().acquireAndLinkBufferRecycler(); + return _getBufferRecyclerPool().acquireAndLinkPooled(); } /** diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java index 3fa35f1273..d24f422465 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java @@ -226,7 +226,7 @@ public void release() { // nullify the reference to the pool in order to avoid the risk of releasing // the same BufferRecycler more than once, thus compromising the pool integrity _pool = null; - tmpPool.releaseBufferRecycler(this); + tmpPool.releasePooled(this); } } } diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index 0f39ebb01c..f7c20848cb 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -24,7 +24,7 @@ *

    • {@link BoundedPool} is "bounded pool" and retains at most N recyclers (default value being * {@link BoundedPool#DEFAULT_CAPACITY}) at any given time. *
    • - *
    • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePool} + *
    • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePoolBase} * -- are "unbounded" and retain any number of recyclers released: in practice * it is at most the highest number of concurrently used {@link BufferRecycler}s. *
    • @@ -49,37 +49,36 @@ public interface WithPool

      > { } /** - * Method called to acquire a {@link BufferRecycler} from this pool + * Method called to acquire a Pooled value from this pool * AND make sure it is linked back to this * {@link BufferRecyclerPool} as necessary for it to be - * released (see {@link #releaseBufferRecycler}) later on after - * usage ends. - * Actual acquisition is done by a call to {@link #acquireBufferRecycler()}. + * released (see {@link #releasePooled}) later after usage ends. + * Actual acquisition is done by a call to {@link #acquirePooled()}. *

      - * Default implementation calls {@link #acquireBufferRecycler()} followed by - * a call to {@link BufferRecycler#withPool}. + * Default implementation calls {@link #acquirePooled()} followed by + * a call to {@link WithPool#withPool}. * - * @return {@link BufferRecycler} for caller to use; caller expected - * to call {@link #releaseBufferRecycler} after it is done using recycler. + * @return Pooled instance for caller to use; caller expected + * to call {@link #releasePooled} after it is done using instance. */ - default P acquireAndLinkBufferRecycler() { - return acquireBufferRecycler().withPool(this); + default P acquireAndLinkPooled() { + return acquirePooled().withPool(this); } /** * Method for sub-classes to implement for actual acquire logic; called - * by {@link #acquireAndLinkBufferRecycler()}. + * by {@link #acquireAndLinkPooled()}. */ - P acquireBufferRecycler(); + P acquirePooled(); /** - * Method that should be called when previously acquired (see {@link #acquireAndLinkBufferRecycler}) - * recycler instances is no longer needed; this lets pool to take ownership + * Method that should be called when previously acquired (see {@link #acquireAndLinkPooled}) + * pooled value that is no longer needed; this lets pool to take ownership * for possible reuse. * - * @param recycler + * @param pooled Pooled instance to release back to pool */ - void releaseBufferRecycler(P recycler); + void releasePooled(P pooled); /* /********************************************************************** @@ -107,16 +106,16 @@ protected ThreadLocalPoolBase() { } // // // Actual API implementation @Override - public P acquireAndLinkBufferRecycler() { + public P acquireAndLinkPooled() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler - return acquireBufferRecycler(); + return acquirePooled(); } @Override - public abstract P acquireBufferRecycler(); + public abstract P acquirePooled(); @Override - public void releaseBufferRecycler(P pooled) { + public void releasePooled(P pooled) { ; // nothing to do, relies on ThreadLocal } } @@ -132,16 +131,16 @@ abstract class NonRecyclingPoolBase

      > implements BufferRecy // // // Actual API implementation @Override - public P acquireAndLinkBufferRecycler() { + public P acquireAndLinkPooled() { // since this pool doesn't do anything on release it doesn't need to be registered on the BufferRecycler - return acquireBufferRecycler(); + return acquirePooled(); } @Override - public abstract P acquireBufferRecycler(); + public abstract P acquirePooled(); @Override - public void releaseBufferRecycler(P pooled) { + public void releasePooled(P pooled) { ; // nothing to do, there is no underlying pool } } @@ -177,6 +176,8 @@ protected Optional> _resolveToShared(StatefulImplBase

      sha } return Optional.empty(); } + + public abstract P createPooled(); } /** @@ -200,10 +201,17 @@ protected ConcurrentDequePoolBase(int serialization) { // // // Actual API implementation @Override - public abstract P acquireBufferRecycler(); + public P acquirePooled() { + P pooled = pool.pollFirst(); + if (pooled == null) { + pooled = createPooled(); + } + return pooled; + } + @Override - public void releaseBufferRecycler(P pooled) { + public void releasePooled(P pooled) { pool.offerLast(pooled); } } @@ -214,69 +222,45 @@ public void releaseBufferRecycler(P pooled) { * Pool is unbounded: see {@link BufferRecyclerPool} for * details on what this means. */ - class LockFreePool extends StatefulImplBase + abstract class LockFreePoolBase

      > + extends StatefulImplBase

      { private static final long serialVersionUID = 1L; - /** - * Globally shared pool instance. - */ - private static final LockFreePool GLOBAL = new LockFreePool(SERIALIZATION_SHARED); - // Needs to be transient to avoid JDK serialization from writing it out - private final transient AtomicReference head; + private final transient AtomicReference> head; // // // Life-cycle (constructors, factory methods) - protected LockFreePool(int serialization) { + protected LockFreePoolBase(int serialization) { super(serialization); head = new AtomicReference<>(); } - /** - * Accessor for getting the globally shared singleton instance. - * Note that if you choose to use this instance, - * pool may be shared by many other {@code JsonFactory} instances. - * - * @return Shared pool instance - */ - public static LockFreePool shared() { - return GLOBAL; - } - - /** - * Accessor for creating and returning a new, non-shared pool instance. - * - * @return Newly constructed, non-shared pool instance - */ - public static LockFreePool nonShared() { - return new LockFreePool(SERIALIZATION_NON_SHARED); - } - // // // Actual API implementation @Override - public BufferRecycler acquireBufferRecycler() { + 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 currentHead = head.get(); + Node

      currentHead = head.get(); if (currentHead == null) { - return new BufferRecycler(); + return createPooled(); } if (head.compareAndSet(currentHead, currentHead.next)) { currentHead.next = null; return currentHead.value; } } - return new BufferRecycler(); + return createPooled(); } @Override - public void releaseBufferRecycler(BufferRecycler bufferRecycler) { - LockFreePool.Node newHead = new LockFreePool.Node(bufferRecycler); + public void releasePooled(P bufferRecycler) { + Node

      newHead = new Node<>(bufferRecycler); for (int i = 0; i < 3; i++) { newHead.next = head.get(); if (head.compareAndSet(newHead.next, newHead)) { @@ -285,20 +269,11 @@ public void releaseBufferRecycler(BufferRecycler bufferRecycler) { } } - // // // JDK serialization support - - /** - * Make sure to re-link to global/shared or non-shared. - */ - protected Object readResolve() { - return _resolveToShared(GLOBAL).orElseGet(() -> nonShared()); - } - - private static class Node { - final BufferRecycler value; - LockFreePool.Node next; + protected static class Node

      { + final P value; + Node

      next; - Node(BufferRecycler value) { + Node(P value) { this.value = value; } } @@ -363,16 +338,21 @@ public static BoundedPool nonShared(int capacity) { // // // Actual API implementation @Override - public BufferRecycler acquireBufferRecycler() { - BufferRecycler bufferRecycler = pool.poll(); - if (bufferRecycler == null) { - bufferRecycler = new BufferRecycler(); + public BufferRecycler acquirePooled() { + BufferRecycler pooled = pool.poll(); + if (pooled == null) { + pooled = createPooled(); } - return bufferRecycler; + return pooled; } @Override - public void releaseBufferRecycler(BufferRecycler bufferRecycler) { + public BufferRecycler createPooled() { + return new BufferRecycler(); + } + + @Override + public void releasePooled(BufferRecycler bufferRecycler) { pool.offer(bufferRecycler); } diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java index da8489bb78..4ce4a063e8 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.util.BufferRecyclerPool.ConcurrentDequePoolBase; +import com.fasterxml.jackson.core.util.BufferRecyclerPool.LockFreePoolBase; /** * Set of {@link BufferRecyclerPool} implementations to be used by the default @@ -42,6 +43,42 @@ public static BufferRecyclerPool nonRecyclingPool() { return NonRecyclingPool.GLOBAL; } + /** + * Accessor for getting the shared/global {@link ConcurrentDequePool} instance. + * + * @return Globally shared instance of {@link NonRecyclingPool}. + */ + public static BufferRecyclerPool sharedConcurrentDequePool() { + return ConcurrentDequePool.GLOBAL; + } + + /** + * Accessor for constructing a new, non-shared {@link ConcurrentDequePool} instance. + * + * @return Globally shared instance of {@link NonRecyclingPool}. + */ + public static BufferRecyclerPool newConcurrentDequePool() { + return ConcurrentDequePool.construct(); + } + + /** + * Accessor for getting the shared/global {@link LockFreePool} instance. + * + * @return Globally shared instance of {@link NonRecyclingPool}. + */ + public static BufferRecyclerPool sharedLockFreePool() { + return LockFreePool.GLOBAL; + } + + /** + * Accessor for constructing a new, non-shared {@link LockFreePool} instance. + * + * @return Globally shared instance of {@link NonRecyclingPool}. + */ + public static BufferRecyclerPool newLockFreePool() { + return LockFreePool.construct(); + } + /* /********************************************************************** /* Concrete BufferRecyclerPool implementations for recycling BufferRecyclers @@ -65,7 +102,7 @@ private ThreadLocalPool() { } @SuppressWarnings("deprecation") @Override - public BufferRecycler acquireBufferRecycler() { + public BufferRecycler acquirePooled() { return BufferRecyclers.getBufferRecycler(); } @@ -88,7 +125,7 @@ public static class NonRecyclingPool protected NonRecyclingPool() { } @Override - public BufferRecycler acquireBufferRecycler() { + public BufferRecycler acquirePooled() { return new BufferRecycler(); } @@ -107,7 +144,7 @@ public static class ConcurrentDequePool extends ConcurrentDequePoolBase construct()); + } + } + + /** + * {@link BufferRecyclerPool} implementation that uses + * a lock free linked list for recycling instances. + * Pool is unbounded: see {@link BufferRecyclerPool} for + * details on what this means. + */ + public static class LockFreePool extends LockFreePoolBase + { + 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 acquireBufferRecycler() { - BufferRecycler bufferRecycler = pool.pollFirst(); - if (bufferRecycler == null) { - bufferRecycler = new BufferRecycler(); - } - return bufferRecycler; + public BufferRecycler createPooled() { + return new BufferRecycler(); } // // // JDK serialization support @@ -150,7 +202,7 @@ public BufferRecycler acquireBufferRecycler() { * Make sure to re-link to global/shared or non-shared. */ protected Object readResolve() { - return _resolveToShared(GLOBAL).orElseGet(() -> nonShared()); + return _resolveToShared(GLOBAL).orElseGet(() -> construct()); } } } diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java index 58474a8b36..1d8f8caeda 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java +++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java @@ -120,14 +120,14 @@ public void testRecyclerPools() throws Exception _testRecyclerPoolGlobal(JsonBufferRecyclers.nonRecyclingPool()); _testRecyclerPoolGlobal(JsonBufferRecyclers.threadLocalPool()); - _testRecyclerPoolGlobal(JsonBufferRecyclers.ConcurrentDequePool.shared()); - _testRecyclerPoolGlobal(BufferRecyclerPool.LockFreePool.shared()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedConcurrentDequePool()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedLockFreePool()); BufferRecyclerPool.BoundedPool bounded = _testRecyclerPoolGlobal(BufferRecyclerPool.BoundedPool.shared()); assertEquals(BufferRecyclerPool.BoundedPool.DEFAULT_CAPACITY, bounded.capacity()); - _testRecyclerPoolNonShared(JsonBufferRecyclers.ConcurrentDequePool.nonShared()); - _testRecyclerPoolNonShared(BufferRecyclerPool.LockFreePool.nonShared()); + _testRecyclerPoolNonShared(JsonBufferRecyclers.newConcurrentDequePool()); + _testRecyclerPoolNonShared(JsonBufferRecyclers.newLockFreePool()); bounded = _testRecyclerPoolNonShared(BufferRecyclerPool.BoundedPool.nonShared(250)); assertEquals(250, bounded.capacity()); } diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index 25f68817dd..ba22b05ecf 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -23,11 +23,11 @@ public void testThreadLocal() throws Exception { } public void testLockFree() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.LockFreePool.nonShared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newLockFreePool(), true); } public void testConcurrentDequeue() throws Exception { - checkBufferRecyclerPoolImpl(JsonBufferRecyclers.ConcurrentDequePool.nonShared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newConcurrentDequePool(), true); } public void testBounded() throws Exception { @@ -47,7 +47,7 @@ private void checkBufferRecyclerPoolImpl(BufferRecyclerPool pool if (checkPooledResource) { // acquire the pooled BufferRecycler again and check if it is the same instance used before - BufferRecycler pooledBufferRecycler = pool.acquireAndLinkBufferRecycler(); + BufferRecycler pooledBufferRecycler = pool.acquireAndLinkPooled(); try { assertSame(usedBufferRecycler, pooledBufferRecycler); } finally { @@ -89,7 +89,7 @@ class TestPool implements BufferRecyclerPool private BufferRecycler bufferRecycler; @Override - public BufferRecycler acquireBufferRecycler() { + public BufferRecycler acquirePooled() { if (bufferRecycler != null) { BufferRecycler tmp = bufferRecycler; this.bufferRecycler = null; @@ -99,7 +99,7 @@ public BufferRecycler acquireBufferRecycler() { } @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releasePooled(BufferRecycler recycler) { this.bufferRecycler = recycler; } } From 3ff604ac91f133d1ef7345070edd0157efaf3f29 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 27 Sep 2023 21:03:23 -0700 Subject: [PATCH 6/7] Convert last pool instance to base/impl set up --- .../jackson/core/util/BufferRecyclerPool.java | 82 +++++-------------- .../core/util/JsonBufferRecyclers.java | 68 +++++++++++++-- .../jackson/core/TestJDKSerializability.java | 9 +- .../core/io/BufferRecyclerPoolTest.java | 2 +- 4 files changed, 90 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java index f7c20848cb..3c96ee1944 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclerPool.java @@ -8,24 +8,24 @@ import java.util.concurrent.atomic.AtomicReference; /** - * API for entity that controls creation and possible reuse of {@link BufferRecycler} - * instances used for recycling of underlying input/output buffers. + * API for entity that controls creation and possible reuse of pooled + * objects (often things like encoding/decoding buffers). *

      - * Also contains partial base implementations for pools that use different strategies on retaining - * recyclers for reuse. For example we have: + * Also contains partial base implementations for pools that use different + * strategies on retaining objects for reuse. For example we have: *

        - *
      • {@link NonRecyclingPoolBase} which does not retain or recycler anything and - * will always simply construct and return new instance when {@code acquireBufferRecycler} - * is called + *
      • {@link NonRecyclingPoolBase} which does not retain or recycle anything and + * will always simply construct and return new instance when + * {@code acquireBufferRecycler} is called *
      • *
      • {@link ThreadLocalPoolBase} which uses {@link ThreadLocal} to retain at most - * 1 recycler per {@link Thread}. + * 1 object per {@link Thread}. *
      • - *
      • {@link BoundedPool} is "bounded pool" and retains at most N recyclers (default value being - * {@link BoundedPool#DEFAULT_CAPACITY}) at any given time. + *
      • {@link BoundedPoolBase} is "bounded pool" and retains at most N objects (default value being + * {@link BoundedPoolBase#DEFAULT_CAPACITY}) at any given time. *
      • *
      • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePoolBase} - * -- are "unbounded" and retain any number of recyclers released: in practice + * -- are "unbounded" and retain any number of objects released: in practice * it is at most the highest number of concurrently used {@link BufferRecycler}s. *
      • *
      @@ -283,63 +283,37 @@ protected static class Node

      { * {@link BufferRecyclerPool} implementation that uses * a bounded queue ({@link ArrayBlockingQueue} for recycling instances. * This is "bounded" pool since it will never hold on to more - * {@link BufferRecycler} instances than its size configuration: - * the default size is {@link BoundedPool#DEFAULT_CAPACITY}. + * pooled instances than its size configuration: + * the default size is {@link BoundedPoolBase#DEFAULT_CAPACITY}. */ - class BoundedPool extends StatefulImplBase + abstract class BoundedPoolBase

      > + extends StatefulImplBase

      { private static final long serialVersionUID = 1L; /** - * Default capacity which limits number of recyclers that are ever + * Default capacity which limits number of items that are ever * retained for reuse. */ public final static int DEFAULT_CAPACITY = 100; - private static final BoundedPool GLOBAL = new BoundedPool(SERIALIZATION_SHARED); - - private final transient ArrayBlockingQueue pool; + private final transient ArrayBlockingQueue

      pool; private final transient int capacity; // // // Life-cycle (constructors, factory methods) - protected BoundedPool(int capacityAsId) { + protected BoundedPoolBase(int capacityAsId) { super(capacityAsId); capacity = (capacityAsId <= 0) ? DEFAULT_CAPACITY : capacityAsId; pool = new ArrayBlockingQueue<>(capacity); } - /** - * Accessor for getting the globally shared singleton instance. - * Note that if you choose to use this instance, - * pool may be shared by many other {@code JsonFactory} instances. - * - * @return Shared pool instance - */ - public static BoundedPool shared() { - return GLOBAL; - } - - /** - * Accessor for creating and returning a new, non-shared pool instance. - * - * @param capacity Maximum capacity of the pool: must be positive number above zero. - * - * @return Newly constructed, non-shared pool instance - */ - public static BoundedPool nonShared(int capacity) { - if (capacity <= 0) { - throw new IllegalArgumentException("capacity must be > 0, was: "+capacity); - } - return new BoundedPool(capacity); - } - // // // Actual API implementation @Override - public BufferRecycler acquirePooled() { - BufferRecycler pooled = pool.poll(); + public P acquirePooled() { + P pooled = pool.poll(); if (pooled == null) { pooled = createPooled(); } @@ -347,24 +321,10 @@ public BufferRecycler acquirePooled() { } @Override - public BufferRecycler createPooled() { - return new BufferRecycler(); - } - - @Override - public void releasePooled(BufferRecycler bufferRecycler) { + public void releasePooled(P bufferRecycler) { pool.offer(bufferRecycler); } - // // // JDK serialization support - - /** - * Make sure to re-link to global/shared or non-shared. - */ - protected Object readResolve() { - return _resolveToShared(GLOBAL).orElseGet(() -> nonShared(_serialization)); - } - // // // Other methods public int capacity() { diff --git a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java index 4ce4a063e8..229d374ad8 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -1,8 +1,10 @@ package com.fasterxml.jackson.core.util; +import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ConcurrentLinkedDeque; import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.util.BufferRecyclerPool.BoundedPoolBase; import com.fasterxml.jackson.core.util.BufferRecyclerPool.ConcurrentDequePoolBase; import com.fasterxml.jackson.core.util.BufferRecyclerPool.LockFreePoolBase; @@ -64,7 +66,7 @@ public static BufferRecyclerPool newConcurrentDequePool() { /** * Accessor for getting the shared/global {@link LockFreePool} instance. * - * @return Globally shared instance of {@link NonRecyclingPool}. + * @return Globally shared instance of {@link LockFreePool}. */ public static BufferRecyclerPool sharedLockFreePool() { return LockFreePool.GLOBAL; @@ -73,12 +75,30 @@ public static BufferRecyclerPool sharedLockFreePool() { /** * Accessor for constructing a new, non-shared {@link LockFreePool} instance. * - * @return Globally shared instance of {@link NonRecyclingPool}. + * @return Globally shared instance of {@link LockFreePool}. */ public static BufferRecyclerPool newLockFreePool() { return LockFreePool.construct(); } + /** + * Accessor for getting the shared/global {@link BoundedPool} instance. + * + * @return Globally shared instance of {@link BoundedPool}. + */ + public static BufferRecyclerPool sharedBoundedPool() { + return BoundedPool.GLOBAL; + } + + /** + * Accessor for constructing a new, non-shared {@link BoundedPool} instance. + * + * @return Globally shared instance of {@link BoundedPool}. + */ + public static BufferRecyclerPool newBoundedPool(int size) { + return BoundedPool.construct(size); + } + /* /********************************************************************** /* Concrete BufferRecyclerPool implementations for recycling BufferRecyclers @@ -172,6 +192,7 @@ protected Object readResolve() { /** * {@link BufferRecyclerPool} implementation that uses * a lock free linked list for recycling instances. + *

      * Pool is unbounded: see {@link BufferRecyclerPool} for * details on what this means. */ @@ -198,11 +219,48 @@ public BufferRecycler createPooled() { // // // JDK serialization support - /** - * Make sure to re-link to global/shared or non-shared. - */ + // Make sure to re-link to global/shared or non-shared. protected Object readResolve() { return _resolveToShared(GLOBAL).orElseGet(() -> construct()); } } + + /** + * {@link BufferRecyclerPool} implementation that uses + * a bounded queue ({@link ArrayBlockingQueue} for recycling instances. + * This is "bounded" pool since it will never hold on to more + * {@link BufferRecycler} instances than its size configuration: + * the default size is {@link BoundedPoolBase#DEFAULT_CAPACITY}. + */ + public static class BoundedPool extends BoundedPoolBase + { + private static final long serialVersionUID = 1L; + + protected static final BoundedPool GLOBAL = new BoundedPool(SERIALIZATION_SHARED); + + // // // Life-cycle (constructors, factory methods) + + protected BoundedPool(int capacityAsId) { + super(capacityAsId); + } + + public static BoundedPool construct(int capacity) { + if (capacity <= 0) { + throw new IllegalArgumentException("capacity must be > 0, was: "+capacity); + } + return new BoundedPool(capacity); + } + + @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(_serialization)); + } + } } diff --git a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java index 1d8f8caeda..2de6f71eae 100644 --- a/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java +++ b/src/test/java/com/fasterxml/jackson/core/TestJDKSerializability.java @@ -122,13 +122,14 @@ public void testRecyclerPools() throws Exception _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedConcurrentDequePool()); _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedLockFreePool()); - BufferRecyclerPool.BoundedPool bounded = - _testRecyclerPoolGlobal(BufferRecyclerPool.BoundedPool.shared()); - assertEquals(BufferRecyclerPool.BoundedPool.DEFAULT_CAPACITY, bounded.capacity()); + JsonBufferRecyclers.BoundedPool bounded = (JsonBufferRecyclers.BoundedPool) + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedBoundedPool()); + assertEquals(BufferRecyclerPool.BoundedPoolBase.DEFAULT_CAPACITY, bounded.capacity()); _testRecyclerPoolNonShared(JsonBufferRecyclers.newConcurrentDequePool()); _testRecyclerPoolNonShared(JsonBufferRecyclers.newLockFreePool()); - bounded = _testRecyclerPoolNonShared(BufferRecyclerPool.BoundedPool.nonShared(250)); + bounded = (JsonBufferRecyclers.BoundedPool) + _testRecyclerPoolNonShared(JsonBufferRecyclers.newBoundedPool(250)); assertEquals(250, bounded.capacity()); } diff --git a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java index ba22b05ecf..2e3e453073 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BufferRecyclerPoolTest.java @@ -31,7 +31,7 @@ public void testConcurrentDequeue() throws Exception { } public void testBounded() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.BoundedPool.nonShared(1), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newBoundedPool(1), true); } public void testPluggingPool() throws Exception { From eae65fac2655e962615ae89d29a143412f054f7d Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Wed, 27 Sep 2023 21:05:06 -0700 Subject: [PATCH 7/7] Mark BufferRecyclers as deprecated --- .../java/com/fasterxml/jackson/core/util/BufferRecyclers.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java index 0e8fffc14b..9dc28efb57 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java +++ b/src/main/java/com/fasterxml/jackson/core/util/BufferRecyclers.java @@ -11,7 +11,10 @@ * @see BufferRecycler * * @since 2.9.2 + * + * @deprecated Since 2.16 recycling aspects are handled via {@link BufferRecyclerPool}. */ +@Deprecated public class BufferRecyclers { /**