|
Duplicate :
|
|
|
Duplicate :
|
|
|
Duplicate :
|
|
|
Relates :
|
FULL PRODUCT VERSION :
A DESCRIPTION OF THE PROBLEM :
As noted at JDK-8049846, the implementation of Java_java_net_SocketInputStream_socketRead0 assumes that read() won't block after poll() reports that a read is possible. This assumption does not hold, as noted on the man page for select (referenced by the man page for poll): Under Linux, select() may report a socket file descriptor as "ready for reading", while nevertheless a subsequent read blocks. This could for example happen when data has arrived but upon examination has wrong checksum and is discarded. There may be other circumstances in which a file descriptor is spuriously reported as ready. Thus it may be safer to use O_NONBLOCK on sockets that should not block.
In production, we hit this about once a day. For testing and reproduction purposes, we can use fault injection to get spurious poll() results on demand.
[This report is probably more appropriate as a comment on JDK-8049846, but commenting requires an account, and obtaining an account does not appear to be an easy task]
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create file called poll.c with contents:
-----
#define _GNU_SOURCE
#include <poll.h>
#include <dlfcn.h>
#include <stdio.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout) {
static int n;
if ((++n & 0x3) == 0) {
// Fault injection: perhaps we should report a spurious readiness notification
int i;
for (i = 0; i < nfds; ++i) {
if (fds[i].events & POLLIN) {
fds[i].revents |= POLLIN;
return 1;
}
}
}
return ((int(*)(struct pollfd*,nfds_t,int))dlsym(RTLD_NEXT, "poll"))(fds, nfds, timeout);
}
-----
2. Create file called OneReaderThread.java with contents:
-----
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class OneReaderThread {
public static void main(String[] args) throws IOException {
@SuppressWarnings("resource")
ServerSocket serverSocket = new ServerSocket(17291);
new ReadingThreadA().start();
Socket client = serverSocket.accept();
OutputStream os = client.getOutputStream();
byte[] writeData = new byte[2];
for (;;) {
waitingForA = true;
long start = System.currentTimeMillis();
while (waitingForA) {
long now = System.currentTimeMillis();
if (now > start + 500) {
// 500ms have passed, which is 10x the read timeout
System.out.println("Should never happen: A is unresponsive");
os.write(writeData);
break;
}
}
}
}
private static volatile boolean waitingForA;
private static final class ReadingThreadA extends Thread {
@Override
public void run() {
try {
@SuppressWarnings("resource")
Socket s = new Socket("localhost", 17291);
s.setSoTimeout(50); // SO_TIMEOUT is set, meaning that reads should not block for more than 50ms
final InputStream is = s.getInputStream();
byte[] readDataA = new byte[2];
for (;;) {
int n = 0;
try {
n = is.read(readDataA);
} catch (IOException e) {
// Ignore
}
System.out.println("A tick (" + n + ")");
waitingForA = false; // This assignment should happen at least once every 50ms
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
-----
3. Compile the C code: gcc -o poll.so -shared poll.c -ldl -fPIC
4. Compile the Java code: javac OneReaderThread.java
5. Run the Java code with the C library preloaded: LD_PRELOAD=./poll.so java -cp . OneReaderThread
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expect an output stream consisting solely of:
A tick (0)
A tick (0)
A tick (0)
A tick (0)
A tick (0)
A tick (0)
A tick (0)
A tick (0)
ACTUAL -
Actual output stream is repetitions of:
Should never happen: A is unresponsive
A tick (2)
A tick (0)
A tick (0)
A tick (0)
Should never happen: A is unresponsive
A tick (2)
A tick (0)
A tick (0)
A tick (0)
REPRODUCIBILITY :
This bug can be reproduced occasionally.
|