ByteBufUtil.isText method should be safe to be called concurrently

Motivation:

ByteBufUtil.isText(...) may produce unexpected results if called concurrently on the same ByteBuffer.

Modifications:

- Don't use internalNioBuffer where it is not safe.
- Add unit test.

Result:

ByteBufUtil.isText is thread-safe.
This commit is contained in:
Norman Maurer 2018-01-30 16:53:29 +01:00
parent e72c197aa3
commit d2bd36fc4c
2 changed files with 47 additions and 2 deletions

View File

@ -1151,7 +1151,7 @@ public final class ByteBufUtil {
CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT); CharsetDecoder decoder = CharsetUtil.decoder(charset, CodingErrorAction.REPORT, CodingErrorAction.REPORT);
try { try {
if (buf.nioBufferCount() == 1) { if (buf.nioBufferCount() == 1) {
decoder.decode(buf.internalNioBuffer(index, length)); decoder.decode(buf.nioBuffer(index, length));
} else { } else {
ByteBuf heapBuffer = buf.alloc().heapBuffer(length); ByteBuf heapBuffer = buf.alloc().heapBuffer(length);
try { try {

View File

@ -19,10 +19,15 @@ import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Test; import org.junit.Test;
import java.nio.Buffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import static io.netty.buffer.Unpooled.unreleasableBuffer; import static io.netty.buffer.Unpooled.unreleasableBuffer;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@ -551,4 +556,44 @@ public class ByteBufUtilTest {
buffer.release(); buffer.release();
} }
} }
@Test
public void testIsTextMultiThreaded() throws Throwable {
final ByteBuf buffer = Unpooled.copiedBuffer("Hello, World!", CharsetUtil.ISO_8859_1);
try {
final AtomicInteger counter = new AtomicInteger(60000);
final AtomicReference<Throwable> errorRef = new AtomicReference<Throwable>();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
while (errorRef.get() == null && counter.decrementAndGet() > 0) {
assertTrue(ByteBufUtil.isText(buffer, CharsetUtil.ISO_8859_1));
}
} catch (Throwable cause) {
errorRef.compareAndSet(null, cause);
}
}
});
threads.add(thread);
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
Throwable error = errorRef.get();
if (error != null) {
throw error;
}
} finally {
buffer.release();
}
}
} }