EPOLL Socket Shutdown Fix

Motivation:
8dbf5d02e5 modified the shutdown code for Socket but did not correctly calculate the change in shutdown state and only applying this change. This is significant because if sockets are being opening and closed quickly and the underlying FD happens to be reused we need to take care that we don't unintentionally change the state of the new FD by acting on an object which represents the old incarnation of that FD.

Modifications:
- Calculate the shutdown change, and only apply what has changed, or exit if no change.

Result:
Socket.shutdown can not inadvertently affect the state of another logical FD.
This commit is contained in:
Scott Mitchell 2016-03-25 10:10:34 -07:00
parent 99c85ef4f5
commit 5eab79a464
2 changed files with 20 additions and 13 deletions

View File

@ -220,18 +220,12 @@ public class FileDescriptor {
return (state & STATE_OUTPUT_SHUTDOWN_MASK) != 0;
}
static boolean shouldAttemptShutdown(int state, boolean read, boolean write) {
return !isClosed(state) && (read && !isInputShutdown(state) || write && !isOutputShutdown(state));
static int inputShutdown(int state) {
return state | STATE_INPUT_SHUTDOWN_MASK;
}
static int calculateShutdownState(int state, boolean read, boolean write) {
if (read) {
state |= STATE_INPUT_SHUTDOWN_MASK;
}
if (write) {
state |= STATE_OUTPUT_SHUTDOWN_MASK;
}
return state;
static int outputShutdown(int state) {
return state | STATE_OUTPUT_SHUTDOWN_MASK;
}
private static native int open(String path);

View File

@ -52,11 +52,24 @@ public final class Socket extends FileDescriptor {
public void shutdown(boolean read, boolean write) throws IOException {
for (;;) {
int state = this.state;
if (!shouldAttemptShutdown(state, read, write)) {
// We need to only shutdown what has not been shutdown yet, and if there is no change we should not
// shutdown anything. This is because if the underlying FD is reused and we still have an object which
// represents the previous incarnation of the FD we need to be sure we don't inadvertently shutdown the
// "new" FD without explicitly having a change.
final int oldState = this.state;
int newState = oldState;
if (read && !isInputShutdown(newState)) {
newState = inputShutdown(newState);
}
if (write && !isOutputShutdown(newState)) {
newState = outputShutdown(newState);
}
// If there is no change in state, then we should not take any action.
if (newState == oldState) {
return;
}
if (casState(state, calculateShutdownState(state, read, write))) {
if (casState(oldState, newState)) {
break;
}
}