Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8081063

(fs) WatchService.take() ignores thread interrupt status if a WatchKey is signalled

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • tbd
    • 8u45
    • core-libs
    • x86_64
    • windows_7

      FULL PRODUCT VERSION :
      java version "1.8.0_45"
      Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
      Java HotSpot(TM) Client VM (build 25.45-b02, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 6.1.7601]

      A DESCRIPTION OF THE PROBLEM :
      If a WatchKey is in the signalled state, WatchService.take() returns it even if the thread on which take() is called has its interrupted flag set.

      Technically the documentation does not seem to specify the behavior here. The take() methods "throws" Javadoc says "InterruptedException - if interrupted while waiting." It could technically be argued that it was never "waiting" since a key was already signalled.

      However this is at odds with how the rest of the Java API works, such as ArrayBlockingQueue, whose take() method prefers to throw an InterruptedException even if an element is available in the queue.

      This is also inconsistent with the WatchService.poll(long, TimeUnit) method, which prefers to throw an InterruptedException rather than return the signalled key.

      NOTE: In a JDK just prior to this (8u30 or 31 I believe it was called), there was a bug where the WatchKey.reset() method caused a thread's interrupt status to be reset, which could also cause infinite waiting if one is depending on stopping the thread by interrupting it. It appears this was silently fixed at some point; I don't know if it was deliberate or a side effect of another fix. I didn't see a bug in my search regarding it or an entry in the release notes of the two subsequent JDK versions. I mention it here just so you can be aware not to reintroduce it sometime.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run the attached test case. It creates a file to cause a WatchKey to become signalled. Then it resets the key without calling pollEvents(). Though there would be no reason to do this in practice, it simulates what would happen if changes were occurring in the file system faster than the polling loop was processing them. Thus a thread may fail to shut down when expected.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The take() method should throw an InterruptedException if the thread is interrupted, shutting down the polling loop.
      ACTUAL -
      The take() method returns the signalled WatchKey, causing the loop to run forever.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      public void test() throws Exception {
      Path temp = Files.createTempDirectory("javatest");
      final WatchService w = FileSystems.getDefault().newWatchService();
      temp.register(w, StandardWatchEventKinds.ENTRY_CREATE);

      Thread th = new Thread("watchThread") {
      @Override
      public void run() {
      try {
      for (;;) {
      System.out.println("before take " + Thread.currentThread().isInterrupted());
      WatchKey k = w.take();
      // There would be no reason in practice to reset the key
      // without polling the events, but this is to simulate what
      // would happen if file changes were happening faster than
      // the loop was handling them.
      //k.pollEvents();
      System.out.println("before reset " + Thread.currentThread().isInterrupted());
      k.reset();
      System.out.println("after reset " + Thread.currentThread().isInterrupted());
      }
      } catch (InterruptedException e) {
      System.out.println("interrupted");
      }
      }
      };

      Files.createFile(temp.resolve("x"));
      th.start();
      th.interrupt();
      th.join();

      System.out.println("done");
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Manually check the thread's interrupted status before and/or after calling take().

            bpb Brian Burkhalter
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: