Respect ChannelConfig.getWriteSpinCount() when using epoll transport
Motivation: The writeSpinCount was ignored in the epoll transport and it just kept on trying writing. This could cause unnessary cpu spinning if a slow remote peer was reading the data very very slow. Modification: - Correctly take writeSpinCount into account when writing. Result: Less cpu spinning when writing to a slow remote peer.
This commit is contained in:
parent
c62e6b676f
commit
111781f38f
@ -235,14 +235,14 @@ abstract class AbstractEpollChannel extends AbstractChannel {
|
|||||||
return localReadAmount;
|
return localReadAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final int doWriteBytes(ByteBuf buf) throws Exception {
|
protected final int doWriteBytes(ByteBuf buf, int writeSpinCount) throws Exception {
|
||||||
int readableBytes = buf.readableBytes();
|
int readableBytes = buf.readableBytes();
|
||||||
int writtenBytes = 0;
|
int writtenBytes = 0;
|
||||||
if (buf.hasMemoryAddress()) {
|
if (buf.hasMemoryAddress()) {
|
||||||
long memoryAddress = buf.memoryAddress();
|
long memoryAddress = buf.memoryAddress();
|
||||||
int readerIndex = buf.readerIndex();
|
int readerIndex = buf.readerIndex();
|
||||||
int writerIndex = buf.writerIndex();
|
int writerIndex = buf.writerIndex();
|
||||||
for (;;) {
|
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||||
int localFlushedAmount = Native.writeAddress(
|
int localFlushedAmount = Native.writeAddress(
|
||||||
fileDescriptor.intValue(), memoryAddress, readerIndex, writerIndex);
|
fileDescriptor.intValue(), memoryAddress, readerIndex, writerIndex);
|
||||||
if (localFlushedAmount > 0) {
|
if (localFlushedAmount > 0) {
|
||||||
@ -252,9 +252,7 @@ abstract class AbstractEpollChannel extends AbstractChannel {
|
|||||||
}
|
}
|
||||||
readerIndex += localFlushedAmount;
|
readerIndex += localFlushedAmount;
|
||||||
} else {
|
} else {
|
||||||
// Returned EAGAIN need to set EPOLLOUT
|
break;
|
||||||
setFlag(Native.EPOLLOUT);
|
|
||||||
return writtenBytes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -264,7 +262,7 @@ abstract class AbstractEpollChannel extends AbstractChannel {
|
|||||||
} else {
|
} else {
|
||||||
nioBuf = buf.nioBuffer();
|
nioBuf = buf.nioBuffer();
|
||||||
}
|
}
|
||||||
for (;;) {
|
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||||
int pos = nioBuf.position();
|
int pos = nioBuf.position();
|
||||||
int limit = nioBuf.limit();
|
int limit = nioBuf.limit();
|
||||||
int localFlushedAmount = Native.write(fileDescriptor.intValue(), nioBuf, pos, limit);
|
int localFlushedAmount = Native.write(fileDescriptor.intValue(), nioBuf, pos, limit);
|
||||||
@ -275,13 +273,15 @@ abstract class AbstractEpollChannel extends AbstractChannel {
|
|||||||
return writtenBytes;
|
return writtenBytes;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Returned EAGAIN need to set EPOLLOUT
|
|
||||||
setFlag(Native.EPOLLOUT);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return writtenBytes;
|
|
||||||
}
|
}
|
||||||
|
if (writtenBytes < readableBytes) {
|
||||||
|
// Returned EAGAIN need to set EPOLLOUT
|
||||||
|
setFlag(Native.EPOLLOUT);
|
||||||
|
}
|
||||||
|
return writtenBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract class AbstractEpollUnsafe extends AbstractUnsafe {
|
protected abstract class AbstractEpollUnsafe extends AbstractUnsafe {
|
||||||
|
@ -70,7 +70,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
* Write bytes form the given {@link ByteBuf} to the underlying {@link java.nio.channels.Channel}.
|
* Write bytes form the given {@link ByteBuf} to the underlying {@link java.nio.channels.Channel}.
|
||||||
* @param buf the {@link ByteBuf} from which the bytes should be written
|
* @param buf the {@link ByteBuf} from which the bytes should be written
|
||||||
*/
|
*/
|
||||||
private boolean writeBytes(ChannelOutboundBuffer in, ByteBuf buf) throws Exception {
|
private boolean writeBytes(ChannelOutboundBuffer in, ByteBuf buf, int writeSpinCount) throws Exception {
|
||||||
int readableBytes = buf.readableBytes();
|
int readableBytes = buf.readableBytes();
|
||||||
if (readableBytes == 0) {
|
if (readableBytes == 0) {
|
||||||
in.remove();
|
in.remove();
|
||||||
@ -78,16 +78,17 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buf.hasMemoryAddress() || buf.nioBufferCount() == 1) {
|
if (buf.hasMemoryAddress() || buf.nioBufferCount() == 1) {
|
||||||
int writtenBytes = doWriteBytes(buf);
|
int writtenBytes = doWriteBytes(buf, writeSpinCount);
|
||||||
in.removeBytes(writtenBytes);
|
in.removeBytes(writtenBytes);
|
||||||
return writtenBytes == readableBytes;
|
return writtenBytes == readableBytes;
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer[] nioBuffers = buf.nioBuffers();
|
ByteBuffer[] nioBuffers = buf.nioBuffers();
|
||||||
return writeBytesMultiple(in, nioBuffers, nioBuffers.length, readableBytes);
|
return writeBytesMultiple(in, nioBuffers, nioBuffers.length, readableBytes, writeSpinCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean writeBytesMultiple(ChannelOutboundBuffer in, IovArray array) throws IOException {
|
private boolean writeBytesMultiple(
|
||||||
|
ChannelOutboundBuffer in, IovArray array, int writeSpinCount) throws IOException {
|
||||||
|
|
||||||
long expectedWrittenBytes = array.size();
|
long expectedWrittenBytes = array.size();
|
||||||
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
||||||
@ -100,11 +101,9 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
boolean done = false;
|
boolean done = false;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int end = offset + cnt;
|
int end = offset + cnt;
|
||||||
for (;;) {
|
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||||
long localWrittenBytes = Native.writevAddresses(fd().intValue(), array.memoryAddress(offset), cnt);
|
long localWrittenBytes = Native.writevAddresses(fd().intValue(), array.memoryAddress(offset), cnt);
|
||||||
if (localWrittenBytes == 0) {
|
if (localWrittenBytes == 0) {
|
||||||
// Returned EAGAIN need to set EPOLLOUT
|
|
||||||
setFlag(Native.EPOLLOUT);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
expectedWrittenBytes -= localWrittenBytes;
|
expectedWrittenBytes -= localWrittenBytes;
|
||||||
@ -127,14 +126,16 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
}
|
}
|
||||||
} while (offset < end && localWrittenBytes > 0);
|
} while (offset < end && localWrittenBytes > 0);
|
||||||
}
|
}
|
||||||
|
if (!done) {
|
||||||
|
setFlag(Native.EPOLLOUT);
|
||||||
|
}
|
||||||
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean writeBytesMultiple(
|
private boolean writeBytesMultiple(
|
||||||
ChannelOutboundBuffer in, ByteBuffer[] nioBuffers,
|
ChannelOutboundBuffer in, ByteBuffer[] nioBuffers,
|
||||||
int nioBufferCnt, long expectedWrittenBytes) throws IOException {
|
int nioBufferCnt, long expectedWrittenBytes, int writeSpinCount) throws IOException {
|
||||||
|
|
||||||
assert expectedWrittenBytes != 0;
|
assert expectedWrittenBytes != 0;
|
||||||
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
final long initialExpectedWrittenBytes = expectedWrittenBytes;
|
||||||
@ -142,11 +143,9 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
boolean done = false;
|
boolean done = false;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int end = offset + nioBufferCnt;
|
int end = offset + nioBufferCnt;
|
||||||
for (;;) {
|
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||||
long localWrittenBytes = Native.writev(fd().intValue(), nioBuffers, offset, nioBufferCnt);
|
long localWrittenBytes = Native.writev(fd().intValue(), nioBuffers, offset, nioBufferCnt);
|
||||||
if (localWrittenBytes == 0) {
|
if (localWrittenBytes == 0) {
|
||||||
// Returned EAGAIN need to set EPOLLOUT
|
|
||||||
setFlag(Native.EPOLLOUT);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
expectedWrittenBytes -= localWrittenBytes;
|
expectedWrittenBytes -= localWrittenBytes;
|
||||||
@ -173,6 +172,9 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
in.removeBytes(initialExpectedWrittenBytes - expectedWrittenBytes);
|
||||||
|
if (!done) {
|
||||||
|
setFlag(Native.EPOLLOUT);
|
||||||
|
}
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +184,8 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
* @param region the {@link DefaultFileRegion} from which the bytes should be written
|
* @param region the {@link DefaultFileRegion} from which the bytes should be written
|
||||||
* @return amount the amount of written bytes
|
* @return amount the amount of written bytes
|
||||||
*/
|
*/
|
||||||
private boolean writeFileRegion(ChannelOutboundBuffer in, DefaultFileRegion region) throws Exception {
|
private boolean writeFileRegion(
|
||||||
|
ChannelOutboundBuffer in, DefaultFileRegion region, int writeSpinCount) throws Exception {
|
||||||
final long regionCount = region.count();
|
final long regionCount = region.count();
|
||||||
if (region.transfered() >= regionCount) {
|
if (region.transfered() >= regionCount) {
|
||||||
in.remove();
|
in.remove();
|
||||||
@ -193,13 +196,11 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
boolean done = false;
|
boolean done = false;
|
||||||
long flushedAmount = 0;
|
long flushedAmount = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||||
final long offset = region.transfered();
|
final long offset = region.transfered();
|
||||||
final long localFlushedAmount =
|
final long localFlushedAmount =
|
||||||
Native.sendfile(fd().intValue(), region, baseOffset, offset, regionCount - offset);
|
Native.sendfile(fd().intValue(), region, baseOffset, offset, regionCount - offset);
|
||||||
if (localFlushedAmount == 0) {
|
if (localFlushedAmount == 0) {
|
||||||
// Returned EAGAIN need to set EPOLLOUT
|
|
||||||
setFlag(Native.EPOLLOUT);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,12 +217,16 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
|
|
||||||
if (done) {
|
if (done) {
|
||||||
in.remove();
|
in.remove();
|
||||||
|
} else {
|
||||||
|
// Returned EAGAIN need to set EPOLLOUT
|
||||||
|
setFlag(Native.EPOLLOUT);
|
||||||
}
|
}
|
||||||
return done;
|
return done;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
protected void doWrite(ChannelOutboundBuffer in) throws Exception {
|
||||||
|
int writeSpinCount = config().getWriteSpinCount();
|
||||||
for (;;) {
|
for (;;) {
|
||||||
final int msgCount = in.size();
|
final int msgCount = in.size();
|
||||||
|
|
||||||
@ -233,7 +238,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
|
|
||||||
// Do gathering write if the outbounf buffer entries start with more than one ByteBuf.
|
// Do gathering write if the outbounf buffer entries start with more than one ByteBuf.
|
||||||
if (msgCount > 1 && in.current() instanceof ByteBuf) {
|
if (msgCount > 1 && in.current() instanceof ByteBuf) {
|
||||||
if (!doWriteMultiple(in)) {
|
if (!doWriteMultiple(in, writeSpinCount)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,26 +246,26 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
// because a user might have triggered another write and flush when we notify his or her
|
// because a user might have triggered another write and flush when we notify his or her
|
||||||
// listeners.
|
// listeners.
|
||||||
} else { // msgCount == 1
|
} else { // msgCount == 1
|
||||||
if (!doWriteSingle(in)) {
|
if (!doWriteSingle(in, writeSpinCount)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean doWriteSingle(ChannelOutboundBuffer in) throws Exception {
|
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||||
// The outbound buffer contains only one message or it contains a file region.
|
// The outbound buffer contains only one message or it contains a file region.
|
||||||
Object msg = in.current();
|
Object msg = in.current();
|
||||||
if (msg instanceof ByteBuf) {
|
if (msg instanceof ByteBuf) {
|
||||||
ByteBuf buf = (ByteBuf) msg;
|
ByteBuf buf = (ByteBuf) msg;
|
||||||
if (!writeBytes(in, buf)) {
|
if (!writeBytes(in, buf, writeSpinCount)) {
|
||||||
// was not able to write everything so break here we will get notified later again once
|
// was not able to write everything so break here we will get notified later again once
|
||||||
// the network stack can handle more writes.
|
// the network stack can handle more writes.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (msg instanceof DefaultFileRegion) {
|
} else if (msg instanceof DefaultFileRegion) {
|
||||||
DefaultFileRegion region = (DefaultFileRegion) msg;
|
DefaultFileRegion region = (DefaultFileRegion) msg;
|
||||||
if (!writeFileRegion(in, region)) {
|
if (!writeFileRegion(in, region, writeSpinCount)) {
|
||||||
// was not able to write everything so break here we will get notified later again once
|
// was not able to write everything so break here we will get notified later again once
|
||||||
// the network stack can handle more writes.
|
// the network stack can handle more writes.
|
||||||
return false;
|
return false;
|
||||||
@ -273,14 +278,14 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean doWriteMultiple(ChannelOutboundBuffer in) throws Exception {
|
private boolean doWriteMultiple(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||||
if (PlatformDependent.hasUnsafe()) {
|
if (PlatformDependent.hasUnsafe()) {
|
||||||
// this means we can cast to IovArray and write the IovArray directly.
|
// this means we can cast to IovArray and write the IovArray directly.
|
||||||
IovArray array = IovArrayThreadLocal.get(in);
|
IovArray array = IovArrayThreadLocal.get(in);
|
||||||
int cnt = array.count();
|
int cnt = array.count();
|
||||||
if (cnt >= 1) {
|
if (cnt >= 1) {
|
||||||
// TODO: Handle the case where cnt == 1 specially.
|
// TODO: Handle the case where cnt == 1 specially.
|
||||||
if (!writeBytesMultiple(in, array)) {
|
if (!writeBytesMultiple(in, array, writeSpinCount)) {
|
||||||
// was not able to write everything so break here we will get notified later again once
|
// was not able to write everything so break here we will get notified later again once
|
||||||
// the network stack can handle more writes.
|
// the network stack can handle more writes.
|
||||||
return false;
|
return false;
|
||||||
@ -293,7 +298,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
|||||||
int cnt = in.nioBufferCount();
|
int cnt = in.nioBufferCount();
|
||||||
if (cnt >= 1) {
|
if (cnt >= 1) {
|
||||||
// TODO: Handle the case where cnt == 1 specially.
|
// TODO: Handle the case where cnt == 1 specially.
|
||||||
if (!writeBytesMultiple(in, buffers, cnt, in.nioBufferSize())) {
|
if (!writeBytesMultiple(in, buffers, cnt, in.nioBufferSize(), writeSpinCount)) {
|
||||||
// was not able to write everything so break here we will get notified later again once
|
// was not able to write everything so break here we will get notified later again once
|
||||||
// the network stack can handle more writes.
|
// the network stack can handle more writes.
|
||||||
return false;
|
return false;
|
||||||
|
@ -88,14 +88,14 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean doWriteSingle(ChannelOutboundBuffer in) throws Exception {
|
protected boolean doWriteSingle(ChannelOutboundBuffer in, int writeSpinCount) throws Exception {
|
||||||
Object msg = in.current();
|
Object msg = in.current();
|
||||||
if (msg instanceof FileDescriptor && Native.sendFd(fd().intValue(), ((FileDescriptor) msg).intValue()) > 0) {
|
if (msg instanceof FileDescriptor && Native.sendFd(fd().intValue(), ((FileDescriptor) msg).intValue()) > 0) {
|
||||||
// File descriptor was written, so remove it.
|
// File descriptor was written, so remove it.
|
||||||
in.remove();
|
in.remove();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.doWriteSingle(in);
|
return super.doWriteSingle(in, writeSpinCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
Loading…
Reference in New Issue
Block a user