Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Object.wait() causes main thread of execution to return from test

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???

like image 407
Ken W Avatar asked Feb 14 '26 21:02

Ken W


1 Answers

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.

like image 133
Praneeth Reddy G Avatar answered Feb 16 '26 11:02

Praneeth Reddy G