I'm in the process of writing some tests to drive development for a side project of mine, and am encountering some very strange Java behavior:
Object.wait() causes the main thread of execution to return and skip all of the following lines of execution, but only the second time it's called in a loop.
The reason I know this is because I'm attempting to write tests without the use of Thread.sleep() because I believe it is generally bad practice to insert these in main threads of execution, especially tests which would later scale and become extremely long-running tasks.
Here is my test:
@Test
public void testSendReceiveAll() throws Exception {
for (String s : (ArrayList<String>)testFiles) {
((FakeSendFileApi) sendFileApi).setSender(new InetSocketAddress(LOCALHOST,
LOCALPORT)).setIncomingFileName(s + incrAppend());
PendingFile pendingFile = new PendingFile(TEST_PATH + s, new InetSocketAddress(LOCALHOST,
LOCALPORT));
SendAction sendAction = new SendAction(pendingFile);
Thread sendActionThread = new Thread(sendAction);
synchronized (sendAction){
sendActionThread.start();
sendAction.wait(TIMEOUT_MS);
}
File file = new File(s + fileAppend);
assertTrue(file.exists());
assertTrue(file.isFile());
assertTrue(file.canRead());
assertTrue(file.delete());
}
}
Explanation of what it does: Iterate over all of the test files and send and receive them all locally. There is a SendAction class which is instantiated and run in the test:
/**
* Attempts to send the specified file to the specified <code>InetSocketAddress</code>.
* The file's path must be specified completely either absolutely or relatively.
*
* @return true if <code>pendingFile</code> was sent successfully in its entirety.
*/
public synchronized void run() {
try {
ServerSocket serverSocket = new ServerSocket(pendingFile.getSender().getPort());
serverSocket.setSoTimeout(socketTimeoutMillis);
// Blocks until a connection is made on specified socket or until TIMEOUT is reached.
Socket socket = serverSocket.accept();
System.out.println("Sending file " + pendingFile.getFileName());
OutputStream outputStream = socket.getOutputStream();
sendByteArray(new RandomAccessFile(pendingFile.getFileName(), "r"), outputStream);
serverSocket.close();
notifyAll();
} catch (IOException e) {
System.err.println(e); // TODO log error appropriately
}
}
The problem: When I hit the synchronized block of the test, and start the thread to send, then wait for a notify from that sendAction, this works the first time in the loop. The second time through however, the test simply passes and exits on the call to
sendAction.wait(TIMEOUT_MS);
This only occurs sometimes, and not others. I have put print statements to see if I can achieve the race condition without debugging and it does send and receive the first file, but doesn't always send and receive the second file. When I put a println() statement just after the sendAction.wait(TIMEOUT_MS); call, it never executes after the second loop iteration.
What gives???
waits should always occur in loops. (Refer to javadoc of Object.wait() for more details).
Maintain a flag to mark the completion of the task and use it in the condition to guard sendAction.wait()
while(!sendAction.finished) {
sendAction.wait(TIMEOUT_MS);
}
set "sendAction.finished" to true before calling the notifyAll()... AND DO THIS IN FINALLY.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With