ChannelOutputStream::write throws RuntimeException on closed channel

XMLWordPrintable

      ADDITIONAL SYSTEM INFORMATION :
      Linux

      A DESCRIPTION OF THE PROBLEM :
      A write on a ChannelOutputStream, acquired via Files.newOutputStream, will race against a concurrent stream close() and easily bypass the expected delivery of ClosedChannelException (IOException), and instead throw a RuntimeException when FileChannelImpl created for the stream is closed asynchronously from another thread.

      The RuntimeException is not expected (an IOException would be preferred)
      FileChannelImpl's return of 0 for !isOpen() is unlikely to match with documentation indicating the FileChannel expected behavior, either for pre-write or in-progress write experiencing a close:
      ```
      ClosedChannelException - If this channel is closed
      AsynchronousCloseException - If another thread closes this channel while the write operation is in progress
      ```

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Compile and run the provided reproducer

      ---------- BEGIN SOURCE ----------
      import java.io.OutputStream;
      import java.nio.file.Files;
      import java.nio.file.Paths;
      import java.nio.channels.ClosedChannelException;

      class closeForWrite {
        public static void main(String[] args) throws Exception {
          long occursIn = triggerRuntimeException();
          System.out.println(String.format("Occurs within %d close() calls", occursIn));
        }

        private static long triggerRuntimeException() {
          long attempts = 0;
          for (;;) {
            try {
              attempts++;
              attempt();
            } catch (ClosedChannelException e) {
              // ignore
            } catch (Exception e) {
              e.printStackTrace();
              return attempts;
            }
          }
        }

        private static void attempt() throws Exception {
          OutputStream out = Files.newOutputStream(Paths.get("testing"));
          Thread thread = new Thread(() -> {
            try {
              Thread.sleep(10);
              out.close();
            } catch (Exception e) {
              e.printStackTrace();
            }
          });
          thread.start();
          try {
            int b = 0;
            for (;;) {
              out.write(b);
            }
          } finally {
            thread.join();
          }
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      serializing writes with closes by stream would probably prevent this.

      FREQUENCY :
      OFTEN

            Assignee:
            Amy Wang
            Reporter:
            Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated:
              Resolved: