EsotericSoftware / kryonet

TCP/UDP client/server library for Java, based on Kryo
BSD 3-Clause "New" or "Revised" License
1.82k stars 419 forks source link

Deadlock in Client.close() #93

Closed georgeto closed 9 years ago

georgeto commented 9 years ago

This scenario occured during execution of the JsonTest, but I'm pretty sure it is also possible in a real enviroment.

To understand this problem, it is important to know, that Selector.selectNow() synchronizes on Selector, Selector.keys() and Selector.selectedKeys().

public int selectNow() throws IOException {
    return lockAndDoSelect(0);
}

private int lockAndDoSelect(long timeout) throws IOException {
    synchronized (this) {
        if (!isOpen())
            throw new ClosedSelectorException();
        synchronized (publicKeys) {
            synchronized (publicSelectedKeys) {
                return doSelect(timeout);
            }
        }
    }
}

[Thread 1]

  1. Client.update() locks selector
  2. Client's received handler sends a tcp message with connection.sendTCP()

[Thread 2]

  1. Client.stop() is called, and calls Client.close()
  2. Client.close() closes the tcp connection
  3. Client.close() grabs updateLock
  4. Client.close() calls selector.selectNow(), and waits for [Thread 1] to unlock selector

[Thread 1]

  1. Cause tcp connection is closed, Client.sendTCP() throws an IOException and calls Client.close()
  2. Client.close() waits for [Thread 2] to unlock updateLock