-
-
Notifications
You must be signed in to change notification settings - Fork 799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
introducing BufferRecyclerPool #1061
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,13 @@ | ||
package tools.jackson.core.io; | ||
|
||
import java.util.Objects; | ||
import java.util.concurrent.atomic.AtomicBoolean; | ||
|
||
import tools.jackson.core.JsonEncoding; | ||
import tools.jackson.core.StreamReadConstraints; | ||
import tools.jackson.core.StreamWriteConstraints; | ||
import tools.jackson.core.util.BufferRecycler; | ||
import tools.jackson.core.util.BufferRecyclerPool; | ||
import tools.jackson.core.util.TextBuffer; | ||
import tools.jackson.core.util.ReadConstrainedTextBuffer; | ||
|
||
|
@@ -15,7 +17,7 @@ | |
* readers and writers are combined under this object. One instance | ||
* is created for each reader and writer. | ||
*/ | ||
public class IOContext | ||
public class IOContext implements AutoCloseable | ||
{ | ||
/* | ||
/********************************************************************** | ||
|
@@ -99,6 +101,8 @@ public class IOContext | |
*/ | ||
protected char[] _nameCopyBuffer; | ||
|
||
private volatile AtomicBoolean closed = new AtomicBoolean(false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. as per later, maybe not needed any atomic type, if really need it - prefer The suggestion for the field updater is due to the number of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not quite sure how one could reduce footprint as |
||
|
||
/* | ||
/********************************************************************** | ||
/* Life-cycle | ||
|
@@ -110,19 +114,18 @@ public class IOContext | |
* | ||
* @param streamReadConstraints constraints for streaming reads | ||
* @param streamWriteConstraints constraints for streaming writes | ||
* @param br BufferRecycler to use, if any ({@code null} if none) | ||
* @param contentRef Input source reference for location reporting | ||
* @param managedResource Whether input source is managed (owned) by Jackson library | ||
* @param enc Encoding in use | ||
*/ | ||
public IOContext(StreamReadConstraints streamReadConstraints, | ||
StreamWriteConstraints streamWriteConstraints, | ||
BufferRecycler br, ContentReference contentRef, boolean managedResource, | ||
ContentReference contentRef, boolean managedResource, | ||
JsonEncoding enc) | ||
{ | ||
_streamReadConstraints = Objects.requireNonNull(streamReadConstraints); | ||
_streamWriteConstraints = Objects.requireNonNull(streamWriteConstraints); | ||
_bufferRecycler = br; | ||
_bufferRecycler = BufferRecyclerPool.borrowBufferRecycler(); | ||
_contentReference = contentRef; | ||
_managedResource = managedResource; | ||
_encoding = enc; | ||
|
@@ -362,4 +365,11 @@ private IllegalArgumentException wrongBuf() { | |
// sanity check failed; trying to return different, smaller buffer. | ||
return new IllegalArgumentException("Trying to release buffer smaller than original"); | ||
} | ||
|
||
@Override | ||
public void close() { | ||
if (closed.compareAndSet(false, true)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is supposed to be concurrent? If not, and just "ordered" you can use a plain bool There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now I implemented it in a concurrent way, but I agree that this constraint can be relaxed if not necessary. |
||
BufferRecyclerPool.offerBufferRecycler(_bufferRecycler); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package tools.jackson.core.util; | ||
|
||
public class BufferRecyclerPool { | ||
|
||
private static final ObjectPool<BufferRecycler> pool = ObjectPool.newLockFreePool(BufferRecycler::new); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok; since this is now global pool (across all factories), there definitely should be maximum size. But then again, having such setting would lead to need for configuration. In fact, now that I think about this, I think pooling (or default implementation at any rate) really needs to be segmented by factory instance -- no buffers should be shared (by default impl) across |
||
|
||
public static BufferRecycler borrowBufferRecycler() { | ||
return pool.borrow(); | ||
} | ||
public static void offerBufferRecycler(BufferRecycler bufferRecycler) { | ||
pool.offer(bufferRecycler); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package tools.jackson.core.util; | ||
|
||
import java.util.Queue; | ||
import java.util.concurrent.LinkedTransferQueue; | ||
import java.util.function.Consumer; | ||
import java.util.function.Supplier; | ||
|
||
public interface ObjectPool<T> extends AutoCloseable { | ||
|
||
T borrow(); | ||
void offer(T t); | ||
|
||
default void withPooledObject(Consumer<T> objectConsumer) { | ||
T t = borrow(); | ||
try { | ||
objectConsumer.accept(t); | ||
} finally { | ||
offer(t); | ||
} | ||
} | ||
|
||
static <T> ObjectPool<T> newLockFreePool(Supplier<T> factory) { | ||
return new LockFreeObjectPool<>(factory); | ||
} | ||
|
||
static <T> ObjectPool<T> newLockFreePool(Supplier<T> factory, Consumer<T> destroyer) { | ||
return new LockFreeObjectPool<>(factory, destroyer); | ||
} | ||
|
||
class LockFreeObjectPool<T> implements ObjectPool<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that any pool to use should have maximum size, as a general principle. |
||
private final Supplier<T> factory; | ||
private final Consumer<T> destroyer; | ||
|
||
private final Queue<T> pool = new LinkedTransferQueue<>(); | ||
|
||
public LockFreeObjectPool(Supplier<T> factory) { | ||
this(factory, null); | ||
} | ||
|
||
public LockFreeObjectPool(Supplier<T> factory, Consumer<T> destroyer) { | ||
this.factory = factory; | ||
this.destroyer = destroyer; | ||
} | ||
|
||
@Override | ||
public T borrow() { | ||
// System.out.println("Before borrow size: " + pool.size()); | ||
T t = pool.poll(); | ||
return t != null ? t : factory.get(); | ||
} | ||
|
||
@Override | ||
public void offer(T t) { | ||
pool.offer(t); | ||
// System.out.println("After offer size: " + pool.size()); | ||
} | ||
|
||
@Override | ||
public void close() throws Exception { | ||
if (destroyer != null) { | ||
pool.forEach(destroyer); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unlikely that we will accept having any public APIs removed - you will ideally need to keep them and possibly deprecate them.
Otherwise, this PR will need to be retargeted and master and aimed at the 3.0 release which has no release date set for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this is targeted at master - but be aware, it could be years before 3.0 is released.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I indeed sent this PR against master, as I expect this to be ready and merged for the 3.0 release. I don't think that we could keep alive both this method and the solution based on the new object pool at the same time, because using one will break the assumptions made by the other. That said my understanding was that the methods and fields starting with an underscore were intended only for internal use and not considered part of the public API, am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, if so I guess this won't be acceptable for our usage purposes in Quarkus. Should I retarget this against 2.16 branch? Also can you clarify the naming convention around method names starting with an underscore? Is it safe to assume that they are not part of the public API?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public means public. @cowtowncoder may consider allowing an exception - he's the BDFL.
Since it's following the underscore naming convention, you might say it is public but not guaranteed not to be removed at short notice (not up to me to make this call though).