コード例 #1
0
  @Override
  public synchronized PathWatchKey register(Path path, Kind<?>[] kinds, Modifier[] modifiers)
      throws IOException {

    PathImpl pathImpl = checkAndCastToPathImpl(path);

    int flags = makeFlagMask(kinds, modifiers);

    // check that user only provided supported flags and modifiers
    int supportedFlags =
        (FLAG_FILTER_ENTRY_CREATE
            | FLAG_FILTER_ENTRY_DELETE
            | FLAG_FILTER_ENTRY_MODIFY
            | FLAG_FILTER_KEY_INVALID);
    if ((flags & ~supportedFlags) != 0)
      throw new UnsupportedOperationException(
          "The given watch event kind or modifier is not supported by this WatchService");

    String pathname = pathImpl.getFile().getAbsolutePath();

    PollingPathWatchKey key = null;

    // request changeLock
    BSD.write(closePipeWriteFd, new byte[1], 1);
    synchronized (changeLock) {
      if (kqueuefd == -1) throw new ClosedWatchServiceException();

      {
        Integer dirfdInteger = dirs.get(pathname);
        if (dirfdInteger != null) key = keys.get(dirfdInteger);
      }

      if (key == null) {
        // no directory file fd registered - we'll need to open
        // one now

        // create file descriptor and watch event first
        boolean success = false;
        int dirfd = -1;
        try {
          dirfd = BSD.open(pathname, BSD.O_RDONLY, 0);
          if (dirfd == -1)
            throw new IOException(
                "error registering the path with the native OS: " + strerror(errno()));
          kevent e = new kevent();
          e.set_ident(dirfd);
          e.set_filter(EVFILT_VNODE);
          e.set_flags((short) (EV_ADD | EV_CLEAR));
          e.set_fflags(NOTE_WRITE | NOTE_DELETE | NOTE_REVOKE);
          int result = kevent(kqueuefd, new kevent[] {e}, null, null);
          // do we need more specific error handling here?
          if (result != 0)
            throw new IOException(
                "error registering the path with the native OS: " + strerror(errno()));

          // create watch key and add it to the key and dirs maps
          key = new PollingPathWatchKey(this, path, 0);
          keys.put(dirfd, key);
          dirs.put(pathname, dirfd);

        } finally {
          // if something went wrong, close descriptors
          if (key == null) {
            if (dirfd != -1) {
              // if the descriptor has been added to the kqueue,
              // it will be removed automatically when the descriptor
              // closes.
              BSD.close(dirfd);
            }
          }
        }
      }

      if (key != null && key.getFlags() != flags) {
        // check if modification flag has changed; moddiff will
        // be +1 if the ENTRY_MODIFIED flag was added, -1 if
        // it was removed (and 0 if it didn't change)
        int moddiff = 0;
        moddiff += (flags & FLAG_FILTER_ENTRY_MODIFY) != 0 ? +1 : 0;
        moddiff += (key.getFlags() & FLAG_FILTER_ENTRY_MODIFY) != 0 ? -1 : 0;

        numKeysRequiringPolling += moddiff;
        key.setFlags(flags);
      }
      // retract request for change lock
      BSD.read(closePipeReadFd, new byte[1], 1);
    }

    // first poll to capture initial state
    key.poll();

    return key;
  }
コード例 #2
0
  private WatchKey pollImpl(long timeout) throws InterruptedException, ClosedWatchServiceException {
    long lastStart = System.currentTimeMillis();
    do {
      // if there is a timeout specified, count down timeout
      if (timeout != -1) {
        long currentTime = System.currentTimeMillis();
        long lastDuration = currentTime - lastStart;
        timeout -= lastDuration;
        if (timeout < 0) timeout = 0;
        lastStart = currentTime;
      }

      kevent[] eventlist = new kevent[32];

      // FIXME: this should be chosen depending on whether we need to
      // poll or not.
      long selectTimeout = timeout;
      if ((timeout == -1 || timeout > pollingIntervalMillis) && numKeysRequiringPolling > 0)
        selectTimeout = pollingIntervalMillis;
      int nread = 0;

      synchronized (changeLock) {
        // if we have pending watches, we're done and return the first one.
        if (pendingWatchKeys.size() > 0) return pendingWatchKeys.remove();

        // check if watch key has been closed
        if (kqueuefd == -1) throw new ClosedWatchServiceException();

        int[] readfds = {closePipeReadFd, kqueuefd};
        int selectResult = select(readfds, null, null, selectTimeout);
        if (selectResult == -1) {
          // check for interruption
          if (BSD.errno() == BSD.EINTR) throw new InterruptedException();
          // otherwise, this is another error that shouldn't occur
          // here.
          String message = BSD.strerror(BSD.errno());
          try {
            close();
          } finally {
            // the message string here is just for debugging
            throw new ClosedWatchServiceException();
          }
        }
        if (readfds[0] == closePipeReadFd) {
          // we have been requested to release the changeLock
          continue;
        }

        // we know now that kevent() will not block, because select() told us so...
        if (readfds[1] == kqueuefd) nread = kevent(kqueuefd, null, eventlist, null);

        if (nread == -1) {
          if (nread == EINTR) throw new InterruptedException();

          try {
            close();
          } finally {
            // catch exception and throw ClosedWatchServiceException instead
            throw new ClosedWatchServiceException();
          }
        }

        if (nread > 0) {
          // go through all kevent structures and update keys
          for (int i = 0; i < nread; ++i) {
            kevent e = eventlist[i];
            int dirfd = (int) e.get_ident();
            int fflags = e.get_fflags();

            PollingPathWatchKey key = keys.get(dirfd);

            // in some cases, the key might not be there any more because
            // it was invalidated (and therefore cancelled) in response to
            // a previous kevent
            if (key == null) continue;

            boolean eventsAdded;

            // check if watch key has become invalid
            if ((fflags & NOTE_REVOKE) != 0) eventsAdded = cancelImpl(key, true);
            else {
              try {
                // poll key's directory
                eventsAdded = key.poll();
              } catch (FileNotFoundException ex) {
                eventsAdded = cancelImpl(key, true);
              }
            }
            if (eventsAdded) queueKey(key);
          }
        } else if (numKeysRequiringPolling > 0) {
          // if we timed out and we have keys that need to be polled

          Set<Entry<Integer, PollingPathWatchKey>> entrySet = keys.entrySet();

          Iterator<Entry<Integer, PollingPathWatchKey>> iterator = entrySet.iterator();

          while (iterator.hasNext()) {
            Entry<Integer, PollingPathWatchKey> entry = iterator.next();

            PollingPathWatchKey key = entry.getValue();

            // only poll keys that have the modification flag set -
            // CREATE/DELETE are flagged in kevent
            if ((key.getFlags() & FLAG_FILTER_ENTRY_MODIFY) == 0) continue;
            boolean eventsAdded;
            try {
              eventsAdded = key.poll();
            } catch (FileNotFoundException ex) {
              eventsAdded = cancelImpl(key, false);

              iterator.remove();
            }
            if (eventsAdded) queueKey(key);
          }
        }

        // now check for pending watch keys again
        if (pendingWatchKeys.size() > 0) return pendingWatchKeys.remove();
      } // synchronized(changeLock)
    } while (timeout > 0 || timeout == -1);

    return null;
  }