JDK-6608965 : (fc) Thread.interrupt causes ClosedByInterruptException, like ThreadDeath
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: 7
  • Priority: P5
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-09-25
  • Updated: 2011-02-16
  • Resolved: 2009-03-20
Description
A DESCRIPTION OF THE REQUEST :
The main reason of declaring "stop()" method deprecated, as I understand, was the impossibility to control a moment when the thread will be stopped. ThreadDeath occurs unexpectedly, maybe, in a critical (synchronized) section, and we have almost no ways to guarantee that the algorithm will be interrupted in a safe moment, without violation of any invariants.

Unfortunately, since the 1.4 version, it seems that you've added the same behavior for "interrupt()" method, in a case when the interrupted algorithm intensively works with file channels. Namely, "interrupt()" leads to unexpected closing all opened channels, and the next I/O operation (reading, writing, mapping) throws ClosedByInterruptException.

Maybe, this behavior is suitable for some modules, for example, for a simple utility alike "xcopy" that just copies a subdirectory. However, there is a wide set of situations when we work with files in the same manner as with RAM and we are absolutely sure that IOException is impossible. The typical example is using temporary files for processing very large amount of data, much greater than available RAM. Another example is image and 3D-model processing: our image processing system operates with hundreds and thousands images, the number of which is limited by disk space only, and we use file mapping for processing them, for example, for filtering and analysis.

In such situations, we always use file channels for accessing existing (maybe temporary) files, placed on a local disk, and an error is possible here only in a case of a serious low-level disk failure. An algorithm may create gigabytes of temporary files, store their some data, process data and remove the files before exiting, in absolutely such manner as allocating large work Java arrays. In this case, any IOException is usually wrapped into IOError and supposed to be impossible (while the disk is operable and not full).

How can we interrupt a thread working with such files in this manner? The only common way, recommended by Sun for interrupting long-working threads, is Thread.interrupt(). But now it leads to almost the same consequences as Thread.stop()! The algorithm is stopped at the nearest "map" or "read" call and leaves the data structures, stored in the files, in an indeterminate, maybe incorrect state. For example, if the thread performs sorting a large array (or transposing a large matrix), stored in a gigabyte file, the array may be damaged: for example, some data block will be lost (replaced by another block). Here the suitable behavior could be interruption at the "safe" moment, when the file is partially sorted (or the next matrix is fully transposed), but when the file contains correct data.

Could you provide some way for file access operation, including reading, writing and mapping, so that the file channels will not be closed by Thread.interrupt call?

JUSTIFICATION :
Now all algorithms, using file channels for internal needs, for example, for storing temporary data, are incompatible with standard "Thread.interrupt()" idiom. This call leads to the same inappropriate results as "Thread.stop()": the thread is interrupted not at the "safe" point, where we check "Thread.interrupted()", but at the nearest file access, even if it is a part of a sequence of file operations, which must not be interrupted to avoid damaging data and violating invariants.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Some ability to disable unexpected (other than "Thread.interrupted()" check) interruption of the thread by "Thread.interrupted()" call, if the thread use file channels to access disk files.
ACTUAL -
If the interruped thread use file channels for any internal needs, then "Thread.interrupt()" works alike "Thread.stop()" and is dangerous due to the same reasons.

---------- BEGIN SOURCE ----------

import java.io.*;
import java.nio.*;
import java.nio.channels.*;

public class Test {
    public static void main(final String[] args) throws InterruptedException {
        final String fileName = args[0];
        Thread thread = new Thread() {
            public void run() {
                try {
                    for (int k = 0; k < 10000000; k++) {
                        RandomAccessFile raf = new RandomAccessFile(fileName, "r");
                        FileChannel fc = raf.getChannel();
                        // fc.map(FileChannel.MapMode.READ_ONLY, 0, raf.length()); // also lead's to problem
                        fc.read(ByteBuffer.allocateDirect(1000000));
                        // - interrupted here, not while the following "Thread.interrupted()" check!
                        raf.close();
                        if (k % 100 == 0) {
                            // let's supppose that correct state for interruption is when k%100==0 only
                            if (Thread.interrupted()) {
                                System.out.println("\rINTERRUPTED!");
                                break;
                            }
                        }
                        System.out.print("\r" + k);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
        thread.start();
        Thread.sleep(5000);
        System.out.println("\rAttempt to interrupt...");
        thread.interrupt();
    }
}

Please compile and call "java Test someFile", where "someFile" is any large file (>1 MB), and wait for 5 seconds.
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Only avoiding "Thread.interrupt()", as well as "Thread.stop()", for any threads excepting ones where we are absolutely sure that they does not work with file channels. To stop such threads, we should always use own volatile flags, that do not affect the behavior of Java I/O subsystem.

Comments
EVALUATION This is an area that was given detailed consideration during JSR-51. The current behavior is by design and intended. There are no plans to change this. For the description it would appear that using Thread.interrupt is not the right solution and that the submitter really just means a flag that is checked at points where all access to the file has completed.
26-09-2007