JDK-8040817 : (se) Potential deadlock between multiple Selector instances and SelectableChannel
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 7u51
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: linux_ubuntu
  • CPU: x86_64
  • Submitted: 2014-04-15
  • Updated: 2018-04-26
  • Resolved: 2018-04-26
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux ... 3.11.0-19-generic #33-Ubuntu SMP Tue Mar 11 18:48:34 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
This is a variant of the problem reported in bug 4744057 that requires multiple selectors.

Like 4744057, deadlock is possible because the selectNow() locks the cancelled key set, then the key lock of a cancelled key, but SelectableChannel.close() obtains the locks in the opposite order.

The test case registers SelectableChannels with  two Selectors. It uses two threads to repeatedly select from each selector, and two other threads which close channels. Combined with some randomization to ensure that the selector cancelled key sets may be stored in different orders, it is easy to create a deadlock such as the following:

Found one Java-level deadlock:
=============================
"pool-1-thread-4":
  waiting to lock monitor 0x00007f6354007668 (object 0x00000000d8a66920, a java.util.HashSet),
  which is held by "pool-1-thread-2"
"pool-1-thread-2":
  waiting to lock monitor 0x00007f6354010838 (object 0x00000000da1339a0, a java.lang.Object),
  which is held by "pool-1-thread-3"
"pool-1-thread-3":
  waiting to lock monitor 0x00007f6350010ee8 (object 0x00000000d8a63e90, a java.util.HashSet),
  which is held by "pool-1-thread-1"
"pool-1-thread-1":
  waiting to lock monitor 0x00007f6350010a18 (object 0x00000000da134810, a java.lang.Object),
  which is held by "pool-1-thread-4"

Java stack information for the threads listed above:
===================================================
"pool-1-thread-4":
	at java.nio.channels.spi.AbstractSelector.cancel(AbstractSelector.java:88)
	- waiting to lock <0x00000000d8a66920> (a java.util.HashSet)
	at java.nio.channels.spi.AbstractSelectionKey.cancel(AbstractSelectionKey.java:73)
	- locked <0x00000000da134910> (a sun.nio.ch.SelectionKeyImpl)
	at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:237)
	- locked <0x00000000da134810> (a java.lang.Object)
	at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:115)
	- locked <0x00000000da134800> (a java.lang.Object)
	at Deadlock$3.run(Deadlock.java:62)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:744)
"pool-1-thread-2":
	at java.nio.channels.spi.AbstractSelectableChannel.removeKey(AbstractSelectableChannel.java:127)
	- waiting to lock <0x00000000da1339a0> (a java.lang.Object)
	at java.nio.channels.spi.AbstractSelector.deregister(AbstractSelector.java:185)
	at sun.nio.ch.EPollSelectorImpl.implDereg(EPollSelectorImpl.java:177)
	at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:150)
	- locked <0x00000000d8a66920> (a java.util.HashSet)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:76)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
	- locked <0x00000000d8a669f0> (a sun.nio.ch.Util$2)
	- locked <0x00000000d8a669e0> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000000d8a668c8> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:106)
	at Deadlock$2.run(Deadlock.java:36)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:744)
"pool-1-thread-3":
	at java.nio.channels.spi.AbstractSelector.cancel(AbstractSelector.java:88)
	- waiting to lock <0x00000000d8a63e90> (a java.util.HashSet)
	at java.nio.channels.spi.AbstractSelectionKey.cancel(AbstractSelectionKey.java:73)
	- locked <0x00000000da133aa0> (a sun.nio.ch.SelectionKeyImpl)
	at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:237)
	- locked <0x00000000da1339a0> (a java.lang.Object)
	at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:115)
	- locked <0x00000000da133990> (a java.lang.Object)
	at Deadlock$3.run(Deadlock.java:62)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:744)
"pool-1-thread-1":
	at java.nio.channels.spi.AbstractSelectableChannel.removeKey(AbstractSelectableChannel.java:127)
	- waiting to lock <0x00000000da134810> (a java.lang.Object)
	at java.nio.channels.spi.AbstractSelector.deregister(AbstractSelector.java:185)
	at sun.nio.ch.EPollSelectorImpl.implDereg(EPollSelectorImpl.java:177)
	at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:150)
	- locked <0x00000000d8a63e90> (a java.util.HashSet)
	at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:76)
	at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:87)
	- locked <0x00000000d8a640b0> (a sun.nio.ch.Util$2)
	- locked <0x00000000d8a64030> (a java.util.Collections$UnmodifiableSet)
	- locked <0x00000000d8a63c00> (a sun.nio.ch.EPollSelectorImpl)
	at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:106)
	at Deadlock$1.run(Deadlock.java:22)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:744)



STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached test case on a multi-core machine. 

It always fails for me on my laptop (Intel(R) Core(TM) i7-3667U CPU @ 2.00GHz) in under a second.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
JVM continues to run, observable by using jstack to check for deadlocks and high CPU.
ACTUAL -
The JVM running the test  will quickly deadlock. This can be verified using jstack.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.IOException;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

public class Deadlock
{
    public static void main(String[] args) throws Exception
    {
        final Selector selector = Selector.open();
        final Selector selector2 = Selector.open();

        final Executor executor = Executors.newFixedThreadPool(4);

        executor.execute(new Runnable() {
            @Override
            public void run() {

                while (true) {
                    try {
                        selector.selectNow();
                        Thread.yield();
                    }
                    catch (Exception e) {
                    }
                }
            }
        });

        executor.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        selector2.selectNow();
                        Thread.yield();
                    }
                    catch (Exception e) {
                    }
                }
            }
        });

        while (true) {
            final SocketChannel channel = SocketChannel.open();
            channel.configureBlocking(false);

            if (toss()) {
                channel.register(selector2, 0, null);
                channel.register(selector, 0, null);
            }
            else {
                channel.register(selector, 0, null);
                channel.register(selector2, 0, null);
            }

            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        channel.close();
                    }
                    catch (IOException e) {
                    }
                }
            });
        }
    }

    private static boolean toss() {
        return Math.random() < 0.5;
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Only workaround we have is to refactor to use a single selector.


Comments
This was resolved by JDK-8201315 so closing this issue.
26-04-2018

JDK-8201315 changed AbstractSelectableChannel.implCloseChannel so that the key's cancel method is no longer invoked while holding keyLock. This means the deadlock reported here does not exist in JDK 11.
26-04-2018

Here's an updated thread dump with JDK 9 to demonstrate the issue. Java stack information for the threads listed above: =================================================== "pool-1-thread-4": at java.nio.channels.spi.AbstractSelector.cancel(AbstractSelector.java:92) - waiting to lock <0x000000076ccc93c8> (a java.util.HashSet) at java.nio.channels.spi.AbstractSelectionKey.cancel(AbstractSelectionKey.java:73) - locked <0x000000076ccd6570> (a sun.nio.ch.SelectionKeyImpl) at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:240) - locked <0x000000076ccd6478> (a java.lang.Object) at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:115) - locked <0x000000076ccd6468> (a java.lang.Object) at Deadlock$3.run(Deadlock.java:62) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:744) "pool-1-thread-1": at java.nio.channels.spi.AbstractSelectableChannel.removeKey(AbstractSelectableChannel.java:131) - waiting to lock <0x000000076ccd6a90> (a java.lang.Object) at java.nio.channels.spi.AbstractSelector.deregister(AbstractSelector.java:188) at sun.nio.ch.EPollSelectorImpl.implDereg(EPollSelectorImpl.java:177) at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:149) - locked <0x000000076ccc93c8> (a java.util.HashSet) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:83) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - locked <0x000000076ccc9578> (a sun.nio.ch.Util$2) - locked <0x000000076ccc94f0> (a java.util.Collections$UnmodifiableSet) - locked <0x000000076ccc90e0> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:105) at Deadlock$1.run(Deadlock.java:22) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:744) "pool-1-thread-3": at java.nio.channels.spi.AbstractSelector.cancel(AbstractSelector.java:92) - waiting to lock <0x000000076cccae38> (a java.util.HashSet) at java.nio.channels.spi.AbstractSelectionKey.cancel(AbstractSelectionKey.java:73) - locked <0x000000076ccd6b88> (a sun.nio.ch.SelectionKeyImpl) at java.nio.channels.spi.AbstractSelectableChannel.implCloseChannel(AbstractSelectableChannel.java:240) - locked <0x000000076ccd6a90> (a java.lang.Object) at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:115) - locked <0x000000076ccd6a80> (a java.lang.Object) at Deadlock$3.run(Deadlock.java:62) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:744) "pool-1-thread-2": at java.nio.channels.spi.AbstractSelectableChannel.removeKey(AbstractSelectableChannel.java:131) - waiting to lock <0x000000076ccd6478> (a java.lang.Object) at java.nio.channels.spi.AbstractSelector.deregister(AbstractSelector.java:188) at sun.nio.ch.EPollSelectorImpl.implDereg(EPollSelectorImpl.java:177) at sun.nio.ch.SelectorImpl.processDeregisterQueue(SelectorImpl.java:149) - locked <0x000000076cccae38> (a java.util.HashSet) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:76) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - locked <0x000000076cccaf08> (a sun.nio.ch.Util$2) - locked <0x000000076cccaef8> (a java.util.Collections$UnmodifiableSet) - locked <0x000000076cccade0> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.selectNow(SelectorImpl.java:105) at Deadlock$2.run(Deadlock.java:36) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:744) Found 1 deadlock.
24-04-2014

Easy to reproduce on 9 also.
17-04-2014

Can reproduce with 7u51 with testcase provided.
17-04-2014