diff --git a/src/main/java/com/fasterxml/jackson/core/JsonFactory.java b/src/main/java/com/fasterxml/jackson/core/JsonFactory.java index 8b14ef3e1c..2edd6b4090 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 @@ -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; } @@ -2150,7 +2150,7 @@ protected JsonGenerator _decorate(JsonGenerator g) { */ public BufferRecycler _getBufferRecycler() { - return _getBufferRecyclerPool().acquireAndLinkBufferRecycler(); + return _getBufferRecyclerPool().acquireAndLinkPooled(); } /** @@ -2159,12 +2159,12 @@ 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) 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/TSFBuilder.java b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java index 8ee09de195..71e461dc61 100644 --- a/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java +++ b/src/main/java/com/fasterxml/jackson/core/TSFBuilder.java @@ -8,7 +8,9 @@ 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.JsonBufferRecyclers; import com.fasterxml.jackson.core.util.JsonGeneratorDecorator; /** @@ -74,7 +76,7 @@ public abstract class TSFBuilder _bufferRecyclerPool; /** * Optional helper object that may decorate input sources, to do @@ -141,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; @@ -169,7 +171,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 +323,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..d24f422465 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,11 +222,11 @@ 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; - 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 deeae7074c..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). *

- * Different pool implementations 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 NonRecyclingPool} which does not retain any recyclers 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 ThreadLocalPool} which uses {@link ThreadLocal} to retain at most - * 1 recycler per {@link Thread}. + *
  • {@link ThreadLocalPoolBase} which uses {@link ThreadLocal} to retain at most + * 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 ConcurrentDequePool}, {@link LockFreePool} - * -- are "unbounded" and retain any number of recyclers released: in practice + *
  • Two implementations -- {@link ConcurrentDequePoolBase}, {@link LockFreePoolBase} + * -- are "unbounded" and retain any number of objects released: in practice * it is at most the highest number of concurrently used {@link BufferRecycler}s. *
  • *
@@ -33,78 +33,63 @@ *

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

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

> extends Serializable { /** - * Method called to acquire a {@link BufferRecycler} from this pool + * Simple add-on interface that poolable entities must implement. + * + * @param

Self type + */ + public interface WithPool

> { + P withPool(BufferRecyclerPool

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 BufferRecycler 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()}. */ - BufferRecycler 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 - */ - void releaseBufferRecycler(BufferRecycler 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()}. + * @param pooled Pooled instance to release back to pool */ - static BufferRecyclerPool threadLocalPool() { - return ThreadLocalPool.shared(); - } - - /** - * @return Globally shared instance of {@link NonRecyclingPool}; same as calling - * {@link NonRecyclingPool#shared()}. - */ - static BufferRecyclerPool nonRecyclingPool() { - return NonRecyclingPool.shared(); - } + void releasePooled(P pooled); /* /********************************************************************** - /* 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 @@ -112,95 +97,52 @@ 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 BufferRecyclerPool 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 BufferRecyclerPool shared() { - return GLOBAL; - } - - // No instances beyond shared one should be constructed - private ThreadLocalPool() { } + protected ThreadLocalPoolBase() { } // // // Actual API implementation @Override - public BufferRecycler 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(); } - @SuppressWarnings("deprecation") @Override - public BufferRecycler acquireBufferRecycler() { - return BufferRecyclers.getBufferRecycler(); - } + public abstract P acquirePooled(); @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releasePooled(P pooled) { ; // nothing to do, relies on ThreadLocal } - - // // // JDK serialization support - - protected Object readResolve() { return GLOBAL; } } /** * {@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 BufferRecyclerPool 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 BufferRecyclerPool shared() { - return GLOBAL; - } - // // // Actual API implementation @Override - public BufferRecycler 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 BufferRecycler acquireBufferRecycler() { - // Could link back to this pool as marker? For now just leave back-ref empty - return new BufferRecycler(); - } + public abstract P acquirePooled(); @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releasePooled(P pooled) { ; // nothing to do, there is no underlying pool } - - // // // JDK serialization support - - protected Object readResolve() { return GLOBAL; } } /** @@ -208,13 +150,14 @@ 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 + 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; @@ -227,12 +170,14 @@ protected StatefulImplBase(int serialization) { _serialization = serialization; } - protected Optional _resolveToShared(BufferRecyclerPool shared) { + protected Optional> _resolveToShared(StatefulImplBase

shared) { if (_serialization == SERIALIZATION_SHARED) { return Optional.of(shared); } return Optional.empty(); } + + public abstract P createPooled(); } /** @@ -241,66 +186,35 @@ protected Optional _resolveToShared(BufferRecyclerPool share *

* 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); - - private final transient Deque pool; - - // // // Life-cycle (constructors, factory methods) + protected final transient Deque

pool; - 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(); + public P acquirePooled() { + P pooled = pool.pollFirst(); + if (pooled == null) { + pooled = createPooled(); } - return bufferRecycler; + return pooled; } + @Override - public void releaseBufferRecycler(BufferRecycler bufferRecycler) { - pool.offerLast(bufferRecycler); - } - - // // // JDK serialization support - - /** - * Make sure to re-link to global/shared or non-shared. - */ - protected Object readResolve() { - return _resolveToShared(GLOBAL).orElseGet(() -> nonShared()); + public void releasePooled(P pooled) { + pool.offerLast(pooled); } -} + } /** * {@link BufferRecyclerPool} implementation that uses @@ -308,69 +222,45 @@ protected Object readResolve() { * 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)) { @@ -379,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()); - } + protected static class Node

{ + final P value; + Node

next; - private static class Node { - final BufferRecycler value; - LockFreePool.Node next; - - Node(BufferRecycler value) { + Node(P value) { this.value = value; } } @@ -402,83 +283,48 @@ private 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 acquireBufferRecycler() { - BufferRecycler bufferRecycler = pool.poll(); - if (bufferRecycler == null) { - bufferRecycler = new BufferRecycler(); + public P acquirePooled() { + P pooled = pool.poll(); + if (pooled == null) { + pooled = createPooled(); } - return bufferRecycler; + return pooled; } @Override - public void releaseBufferRecycler(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/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 { /** 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..229d374ad8 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/core/util/JsonBufferRecyclers.java @@ -0,0 +1,266 @@ +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; + +/** + * 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(); + } + /** + * 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.GLOBAL; + } + + /** + * 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.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 LockFreePool}. + */ + public static BufferRecyclerPool sharedLockFreePool() { + return LockFreePool.GLOBAL; + } + + /** + * Accessor for constructing a new, non-shared {@link LockFreePool} instance. + * + * @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 + /********************************************************************** + */ + + /** + * {@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; + + protected static final ThreadLocalPool GLOBAL = new ThreadLocalPool(); + + private ThreadLocalPool() { } + + @SuppressWarnings("deprecation") + @Override + public BufferRecycler acquirePooled() { + return BufferRecyclers.getBufferRecycler(); + } + + // // // JDK serialization support + + 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; + + protected static final NonRecyclingPool GLOBAL = new NonRecyclingPool(); + + protected NonRecyclingPool() { } + + @Override + public BufferRecycler acquirePooled() { + 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; + + protected static final ConcurrentDequePool GLOBAL = new ConcurrentDequePool(SERIALIZATION_SHARED); + + // // // Life-cycle (constructors, factory methods) + + protected ConcurrentDequePool(int serialization) { + super(serialization); + } + + public static ConcurrentDequePool construct() { + return new ConcurrentDequePool(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 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 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 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 d03740d152..2de6f71eae 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) @@ -116,22 +117,23 @@ public void testRecyclerPools() throws Exception { // First: shared/global pools that will always remain/become globally // shared instances - _testRecyclerPoolGlobal(BufferRecyclerPool.nonRecyclingPool()); - _testRecyclerPoolGlobal(BufferRecyclerPool.threadLocalPool()); - - _testRecyclerPoolGlobal(BufferRecyclerPool.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(BufferRecyclerPool.LockFreePool.nonShared()); - bounded = _testRecyclerPoolNonShared(BufferRecyclerPool.BoundedPool.nonShared(250)); + _testRecyclerPoolGlobal(JsonBufferRecyclers.nonRecyclingPool()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.threadLocalPool()); + + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedConcurrentDequePool()); + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedLockFreePool()); + JsonBufferRecyclers.BoundedPool bounded = (JsonBufferRecyclers.BoundedPool) + _testRecyclerPoolGlobal(JsonBufferRecyclers.sharedBoundedPool()); + assertEquals(BufferRecyclerPool.BoundedPoolBase.DEFAULT_CAPACITY, bounded.capacity()); + + _testRecyclerPoolNonShared(JsonBufferRecyclers.newConcurrentDequePool()); + _testRecyclerPoolNonShared(JsonBufferRecyclers.newLockFreePool()); + bounded = (JsonBufferRecyclers.BoundedPool) + _testRecyclerPoolNonShared(JsonBufferRecyclers.newBoundedPool(250)); 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); @@ -139,7 +141,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 4bdbac5dd3..2e3e453073 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; @@ -14,30 +15,31 @@ 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 { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.ThreadLocalPool.shared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.threadLocalPool(), true); } public void testLockFree() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.LockFreePool.nonShared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newLockFreePool(), true); } public void testConcurrentDequeue() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.ConcurrentDequePool.nonShared(), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newConcurrentDequePool(), true); } public void testBounded() throws Exception { - checkBufferRecyclerPoolImpl(BufferRecyclerPool.BoundedPool.nonShared(1), true); + checkBufferRecyclerPoolImpl(JsonBufferRecyclers.newBoundedPool(1), true); } 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(); @@ -45,7 +47,7 @@ private void checkBufferRecyclerPoolImpl(BufferRecyclerPool pool, boolean checkP 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 { @@ -82,12 +84,12 @@ private static class NopOutputStream extends OutputStream { @SuppressWarnings("serial") - class TestPool implements BufferRecyclerPool + class TestPool implements BufferRecyclerPool { private BufferRecycler bufferRecycler; @Override - public BufferRecycler acquireBufferRecycler() { + public BufferRecycler acquirePooled() { if (bufferRecycler != null) { BufferRecycler tmp = bufferRecycler; this.bufferRecycler = null; @@ -97,7 +99,7 @@ public BufferRecycler acquireBufferRecycler() { } @Override - public void releaseBufferRecycler(BufferRecycler recycler) { + public void releasePooled(BufferRecycler recycler) { this.bufferRecycler = recycler; } }