@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; }
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; }