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:
parent
ad85014110
commit
4d0e76e561
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user