Skip to content

Commit

Permalink
Fix #479 (thread-safete for BufferRecycler)
Browse files Browse the repository at this point in the history
  • Loading branch information
cowtowncoder committed Aug 27, 2019
1 parent f21edfc commit 2c8f025
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 19 deletions.
2 changes: 2 additions & 0 deletions release-notes/VERSION-2.x
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ JSON library.

2.10.0.pr2

#479: Improve thread-safety of buffer recycling to enable recycling again
for async parsing
#517: Add `JsonGenerator.writeStartObject(Object, int)` (needed by CBOR, maybe Avro)
#549: Add configurability of "quote character" for JSON factory
- Rewrite `JsonGenerator.copyCurrentStructure()` to remove recursion)
Expand Down
7 changes: 3 additions & 4 deletions src/main/java/com/fasterxml/jackson/core/JsonFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -1727,10 +1727,9 @@ protected IOContext _createContext(Object srcRef, boolean resourceManaged) {
* @since 2.9.7
*/
protected IOContext _createNonBlockingContext(Object srcRef) {
// [jackson-core#476]: disable buffer recycling for 2.9 to avoid concurrency issues;
// easiest done by just constructing private "recycler":
BufferRecycler recycler = new BufferRecycler();
return new IOContext(recycler, srcRef, false);
// [jackson-core#479]: allow recycling for non-blocking parser again
// now that access is thread-safe
return new IOContext(_getBufferRecycler(), srcRef, false);
}

/*
Expand Down
34 changes: 19 additions & 15 deletions src/main/java/com/fasterxml/jackson/core/util/BufferRecycler.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.fasterxml.jackson.core.util;

import java.util.concurrent.atomic.AtomicReferenceArray;

/**
* This is a small utility class, whose main functionality is to allow
* simple reuse of raw byte/char buffers. It is usually used through
* <code>ThreadLocal</code> member of the owning class pointing to
* instance of this class through a <code>SoftReference</code>. The
* end result is a low-overhead GC-cleanable recycling: hopefully
* ideal for use by stream readers.
*<p>
* Rewritten in 2.10 to be thread-safe (see [jackson-core#479] for details),
* to not rely on {@code ThreadLocal} access.
*/
public class BufferRecycler
{
Expand Down Expand Up @@ -70,16 +75,19 @@ public class BufferRecycler

private final static int[] BYTE_BUFFER_LENGTHS = new int[] { 8000, 8000, 2000, 2000 };
private final static int[] CHAR_BUFFER_LENGTHS = new int[] { 4000, 4000, 200, 200 };

final protected byte[][] _byteBuffers;
final protected char[][] _charBuffers;

// Note: changed from simple array in 2.10:
protected final AtomicReferenceArray<byte[]> _byteBuffers;

// Note: changed from simple array in 2.10:
protected final AtomicReferenceArray<char[]> _charBuffers;

/*
/**********************************************************
/* Construction
/**********************************************************
*/

/**
* Default constructor used for creating instances of this default
* implementation.
Expand All @@ -95,8 +103,8 @@ public BufferRecycler() {
* @since 2.4
*/
protected BufferRecycler(int bbCount, int cbCount) {
_byteBuffers = new byte[bbCount][];
_charBuffers = new char[cbCount][];
_byteBuffers = new AtomicReferenceArray<byte[]>(bbCount);
_charBuffers = new AtomicReferenceArray<char[]>(cbCount);
}

/*
Expand All @@ -117,17 +125,15 @@ public byte[] allocByteBuffer(int ix, int minSize) {
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
byte[] buffer = _byteBuffers[ix];
byte[] buffer = _byteBuffers.getAndSet(ix, null);
if (buffer == null || buffer.length < minSize) {
buffer = balloc(minSize);
} else {
_byteBuffers[ix] = null;
}
return buffer;
}

public void releaseByteBuffer(int ix, byte[] buffer) {
_byteBuffers[ix] = buffer;
_byteBuffers.set(ix, buffer);
}

/*
Expand All @@ -145,17 +151,15 @@ public char[] allocCharBuffer(int ix, int minSize) {
if (minSize < DEF_SIZE) {
minSize = DEF_SIZE;
}
char[] buffer = _charBuffers[ix];
char[] buffer = _charBuffers.getAndSet(ix, null);
if (buffer == null || buffer.length < minSize) {
buffer = calloc(minSize);
} else {
_charBuffers[ix] = null;
}
return buffer;
}

public void releaseCharBuffer(int ix, char[] buffer) {
_charBuffers[ix] = buffer;
_charBuffers.set(ix, buffer);
}

/*
Expand All @@ -171,7 +175,7 @@ protected int byteBufferLength(int ix) {
protected int charBufferLength(int ix) {
return CHAR_BUFFER_LENGTHS[ix];
}

/*
/**********************************************************
/* Actual allocations separated for easier debugging/profiling
Expand Down

0 comments on commit 2c8f025

Please sign in to comment.