JDK-8012019 : (fc) Thread.interrupt triggers hang in FileChannelImpl.pread (win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 7,7u17
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2013-04-04
  • Updated: 2013-06-26
  • Resolved: 2013-04-17
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u40Fixed 8 b87Fixed
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_17 " 
Java(TM) SE Runtime Environment (build 1.7.0_17-b02)
Java HotSpot(TM) 64-Bit Server VM (build 23.7-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
Our code frequently locks up when concurrent threads are reading the same file through NIO. I have created a test class that can be used to reproduce the issue. This is an example thread dump when the deadlock occurs:

-----------------------------------------------------------------------
 " test thread 2 "  prio=6 tid=0x01178400 nid=0x4b0 waiting for monitor entry [0x1967f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at sun.nio.ch.FileDispatcherImpl.pread(FileDispatcherImpl.java:61)
- waiting to lock <0x09d00c58> (a java.lang.Object)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:222)
at sun.nio.ch.IOUtil.read(IOUtil.java:198)
at sun.nio.ch.FileChannelImpl.read(FileChannelImpl.java:674)
at NioDeadlockTest$1.run(NioDeadlockTest.java:100)
at java.lang.Thread.run(Thread.java:722)

   Locked ownable synchronizers:
- None

 " test thread 1 "  prio=6 tid=0x01177c00 nid=0xd08 in Object.wait() [0x194ef000]
   java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x09d00db8> (a sun.nio.ch.NativeThreadSet)
at java.lang.Object.wait(Object.java:503)
at sun.nio.ch.NativeThreadSet.signalAndWait(NativeThreadSet.java:102)
- locked <0x09d00db8> (a sun.nio.ch.NativeThreadSet)
at sun.nio.ch.FileChannelImpl.implCloseChannel(FileChannelImpl.java:114)
at java.nio.channels.spi.AbstractInterruptibleChannel$1.interrupt(AbstractInterruptibleChannel.java:165)
- locked <0x09d00dd0> (a java.lang.Object)
at java.nio.channels.spi.AbstractInterruptibleChannel.begin(AbstractInterruptibleChannel.java:173)
at sun.nio.ch.FileChannelImpl.size(FileChannelImpl.java:289)
- locked <0x09d00c58> (a java.lang.Object)
at NioDeadlockTest$1.run(NioDeadlockTest.java:102)
at java.lang.Thread.run(Thread.java:722)

   Locked ownable synchronizers:
- None

 " main "  prio=6 tid=0x0037ec00 nid=0x1a40 waiting for monitor entry [0x00f0f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
at java.nio.channels.spi.AbstractInterruptibleChannel$1.interrupt(AbstractInterruptibleChannel.java:160)
- waiting to lock <0x09d00dd0> (a java.lang.Object)
at java.lang.Thread.interrupt(Thread.java:935)
- locked <0x09d01410> (a java.lang.Object)
at NioDeadlockTest.stopThread(NioDeadlockTest.java:81)
at NioDeadlockTest.runTest(NioDeadlockTest.java:68)
at NioDeadlockTest.main(NioDeadlockTest.java:25)

   Locked ownable synchronizers:
- None
-----------------------------------------------------------------------

In this case,  " test thread 1 "  has been interrupted by the main thread and is attempting to close the file channel. It is waiting in NativeThreadSet.signalAndWait() for 'used' to count down to 0.  " test thread 2 "  is the one that is supposed to make this change by calling NativeThreadSet.remove(), but that thread is waiting for a lock (FileChannelImpl.positionLock) that is held by the first test thread. Finally, the main thread is also blocked, waiting for another lock that is held by the first test thread.

This test fails with both the x64 and x86 versions of 1.7.0_17-b02, but it runs fine with 1.6.0_35-b10.

REGRESSION.  Last worked in version 6u31

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the supplied test class NioDeadlockTest.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test class should complete the 100 test runs successfully.
ACTUAL -
The test freezes, often in the first 10 attempts.

REPRODUCIBILITY :
This bug can be reproduced often.

---------- BEGIN SOURCE ----------
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioDeadlockTest {

public static void main(String[] args)
throws Exception
{
File tmpFile = File.createTempFile( " deadlock " ,  " .txt " );
try {
writeSomeText(tmpFile);

for (int i = 1; i <= 100; i++) {
System.out.println( " Attempt  "  + i);
NioDeadlockTest test = new NioDeadlockTest(tmpFile);
test.runTest();
test.close();
}
}
finally {
tmpFile.delete();
}
}

private static void writeSomeText(File file)
throws IOException
{
FileWriter fw = new FileWriter(file);
try {
for (int i = 0; i < 1024; i++) {
fw.write( " I shall never again run into a deadlock!
 " );
}
}
finally {
fw.close();
}
}

private final RandomAccessFile raf;

private final FileChannel fc;

public NioDeadlockTest(File file)
throws FileNotFoundException
{
raf = new RandomAccessFile(file,  " r " );
fc = raf.getChannel();
}

public void runTest()
throws InterruptedException
{
Thread t1 = startTestThread(1);
Thread t2 = startTestThread(2);

Thread.sleep(100);

stopThread(t1);
stopThread(t2);
}

public void close()
throws IOException
{
raf.close();
}

private void stopThread(Thread t)
throws InterruptedException
{
System.out.println( " Interrupting thread  "  + t.getName());
t.interrupt();
t.join(5000);
if (t.isAlive()) {
System.out.println( " Failed to stop thread  "  + t.getName() +  " ; deadlock? " );
}
}

private Thread startTestThread(final int id) {
Thread thread = new Thread(new Runnable() {

private final ByteBuffer bb = ByteBuffer.allocate(1024);

@Override
public void run() {
System.out.println( " > "  + id);
try {
long pos = 0;
while (true) {
bb.clear();
fc.read(bb, pos);
pos += 1024;
if (pos > fc.size()) {
pos = 0;
}
}
}
catch (IOException e) {
System.out.println(id +  " :  "  + e.getClass());
}
finally {
System.out.println( " < "  + id);
}
}
});
thread.setName( " test thread  "  + id);
thread.start();
return thread;
}
}

---------- END SOURCE ----------
Comments
verified in b94
18-06-2013

This is issue is not specific/new in a 7 update, it duplicates with 7 GA too. The issue is specific to Windows and where a read/write method that takes a position is in use. If another concurrent position sensitive method is interrupted at around this time then it can deadlock as described. We need to adjust the synchronization so that the positionLock is acquired earlier. This is tricky to get right and needs to be bake in a major release before we consider fixing this in a 7 update.
15-04-2013