[#3709] Ensure all data is read from socket when EPOLLRDUP is received

Motivation:

When EPOLLRDHUP is received we need to try to read at least one time to ensure
that we read all pending data from the socket. Otherwise we may loose data.

Modifications:

- Ensure we read all data from socket
- Ensure file descriptor is closed on doClose() even if doDeregister() throws an Exception.
- Only handle either EPOLLRDHUP or EPOLLIN as only one is needed to detect connection reset.

Result:

No more data loss on connection reset.
This commit is contained in:
Norman Maurer 2015-05-04 15:40:29 +02:00
parent b3abd58b05
commit 08e4b076be
3 changed files with 23 additions and 22 deletions

View File

@ -99,13 +99,15 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
@Override @Override
protected void doClose() throws Exception { protected void doClose() throws Exception {
active = false; active = false;
try {
// deregister from epoll now // deregister from epoll now
doDeregister(); doDeregister();
} finally {
// Ensure the file descriptor is closed in all cases.
FileDescriptor fd = fileDescriptor; FileDescriptor fd = fileDescriptor;
fd.close(); fd.close();
} }
}
@Override @Override
protected void doDisconnect() throws Exception { protected void doDisconnect() throws Exception {

View File

@ -765,12 +765,15 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
@Override @Override
void epollRdHupReady() { void epollRdHupReady() {
// Just call closeOnRead(). There is no need to trigger a read as this if (isActive()) {
// will result in an IOException anyway. // If it is still active, we need to call epollInReady as otherwise we may miss to
// // read pending data from the underyling file descriptor.
// See https://github.com/netty/netty/issues/3539 // See https://github.com/netty/netty/issues/3709
epollInReady();
} else {
closeOnRead(pipeline()); closeOnRead(pipeline());
} }
}
@Override @Override
void epollInReady() { void epollInReady() {

View File

@ -310,14 +310,15 @@ final class EpollEventLoop extends SingleThreadEventLoop {
final long ev = events.events(i); final long ev = events.events(i);
AbstractEpollChannel ch = channels.get(fd); AbstractEpollChannel ch = channels.get(fd);
if (ch != null && ch.isOpen()) { if (ch != null) {
AbstractEpollUnsafe unsafe = (AbstractEpollUnsafe) ch.unsafe(); AbstractEpollUnsafe unsafe = (AbstractEpollUnsafe) ch.unsafe();
// We need to check if the channel is still open before try to trigger the // First check if EPOLLIN was set, in this case we do not need to check for
// callbacks. // EPOLLRDHUP as EPOLLIN will handle connection-reset case as well.
// See https://github.com/netty/netty/issues/3443 if ((ev & Native.EPOLLIN) != 0) {
if ((ev & Native.EPOLLRDHUP) != 0 && ch.isOpen()) { // Something is ready to read, so consume it now
unsafe.epollInReady();
} else if ((ev & Native.EPOLLRDHUP) != 0) {
unsafe.epollRdHupReady(); unsafe.epollRdHupReady();
} }
@ -325,11 +326,6 @@ final class EpollEventLoop extends SingleThreadEventLoop {
// force flush of data as the epoll is writable again // force flush of data as the epoll is writable again
unsafe.epollOutReady(); unsafe.epollOutReady();
} }
if ((ev & Native.EPOLLIN) != 0 && ch.isOpen()) {
// Something is ready to read, so consume it now
unsafe.epollInReady();
}
} else { } else {
// We received an event for an fd which we not use anymore. Remove it from the epoll_event set. // We received an event for an fd which we not use anymore. Remove it from the epoll_event set.
try { try {