ChunkedNioFile can use absolute FileChannel::read to read chunks (#9592)

Motivation:

Users can reuse the same FileChannel for different ChunkedNioFile
instances without being worried that FileChannel::position will be
changed concurrently by them.
In addition, FileChannel::read with absolute position allows to
use on *nix pread that is more efficient then fread.

Modifications:

Always use absolute FileChannel::read ops

Result:

Faster and more flexible uses of FileChannel for ChunkedNioFile
This commit is contained in:
Francesco Nigro 2019-09-24 07:17:09 +02:00 committed by Norman Maurer
parent 76592db0bd
commit eb3c4bd926
2 changed files with 43 additions and 4 deletions

View File

@ -23,6 +23,7 @@ import io.netty.channel.FileRegion;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel; import java.nio.channels.FileChannel;
/** /**
@ -101,9 +102,8 @@ public class ChunkedNioFile implements ChunkedInput<ByteBuf> {
"chunkSize: " + chunkSize + "chunkSize: " + chunkSize +
" (expected: a positive integer)"); " (expected: a positive integer)");
} }
if (!in.isOpen()) {
if (offset != 0) { throw new ClosedChannelException();
in.position(offset);
} }
this.in = in; this.in = in;
this.chunkSize = chunkSize; this.chunkSize = chunkSize;
@ -161,7 +161,7 @@ public class ChunkedNioFile implements ChunkedInput<ByteBuf> {
try { try {
int readBytes = 0; int readBytes = 0;
for (;;) { for (;;) {
int localReadBytes = buffer.writeBytes(in, chunkSize - readBytes); int localReadBytes = buffer.writeBytes(in, offset + readBytes, chunkSize - readBytes);
if (localReadBytes < 0) { if (localReadBytes < 0) {
break; break;
} }

View File

@ -26,13 +26,17 @@ import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.Channels; import java.nio.channels.Channels;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -102,6 +106,41 @@ public class ChunkedWriteHandlerTest {
check(new ChunkedNioFile(TMP), new ChunkedNioFile(TMP), new ChunkedNioFile(TMP)); check(new ChunkedNioFile(TMP), new ChunkedNioFile(TMP), new ChunkedNioFile(TMP));
} }
@Test
public void testChunkedNioFileLeftPositionUnchanged() throws IOException {
FileChannel in = null;
final long expectedPosition = 10;
try {
in = new RandomAccessFile(TMP, "r").getChannel();
in.position(expectedPosition);
check(new ChunkedNioFile(in) {
@Override
public void close() throws Exception {
//no op
}
});
Assert.assertTrue(in.isOpen());
Assert.assertEquals(expectedPosition, in.position());
} finally {
if (in != null) {
in.close();
}
}
}
@Test(expected = ClosedChannelException.class)
public void testChunkedNioFileFailOnClosedFileChannel() throws IOException {
final FileChannel in = new RandomAccessFile(TMP, "r").getChannel();
in.close();
check(new ChunkedNioFile(in) {
@Override
public void close() throws Exception {
//no op
}
});
Assert.fail();
}
@Test @Test
public void testUnchunkedData() throws IOException { public void testUnchunkedData() throws IOException {
check(Unpooled.wrappedBuffer(BYTES)); check(Unpooled.wrappedBuffer(BYTES));