Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/sun/nio/fs/LinuxWatchService.java
32288 views
/*1* Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.nio.fs;2627import java.nio.file.*;28import java.security.AccessController;29import java.security.PrivilegedAction;30import java.util.*;31import java.io.IOException;32import sun.misc.Unsafe;3334import static sun.nio.fs.UnixNativeDispatcher.*;35import static sun.nio.fs.UnixConstants.*;3637/**38* Linux implementation of WatchService based on inotify.39*40* In summary a background thread polls inotify plus a socket used for the wakeup41* mechanism. Requests to add or remove a watch, or close the watch service,42* cause the thread to wakeup and process the request. Events are processed43* by the thread which causes it to signal/queue the corresponding watch keys.44*/4546class LinuxWatchService47extends AbstractWatchService48{49private static final Unsafe unsafe = Unsafe.getUnsafe();5051// background thread to read change events52private final Poller poller;5354LinuxWatchService(UnixFileSystem fs) throws IOException {55// initialize inotify56int ifd = - 1;57try {58ifd = inotifyInit();59} catch (UnixException x) {60String msg = (x.errno() == EMFILE) ?61"User limit of inotify instances reached or too many open files" :62x.errorString();63throw new IOException(msg);64}6566// configure inotify to be non-blocking67// create socketpair used in the close mechanism68int sp[] = new int[2];69try {70configureBlocking(ifd, false);71socketpair(sp);72configureBlocking(sp[0], false);73} catch (UnixException x) {74UnixNativeDispatcher.close(ifd);75throw new IOException(x.errorString());76}7778this.poller = new Poller(fs, this, ifd, sp);79this.poller.start();80}8182@Override83WatchKey register(Path dir,84WatchEvent.Kind<?>[] events,85WatchEvent.Modifier... modifiers)86throws IOException87{88// delegate to poller89return poller.register(dir, events, modifiers);90}9192@Override93void implClose() throws IOException {94// delegate to poller95poller.close();96}9798/**99* WatchKey implementation100*/101private static class LinuxWatchKey extends AbstractWatchKey {102// inotify descriptor103private final int ifd;104// watch descriptor105private volatile int wd;106107LinuxWatchKey(UnixPath dir, LinuxWatchService watcher, int ifd, int wd) {108super(dir, watcher);109this.ifd = ifd;110this.wd = wd;111}112113int descriptor() {114return wd;115}116117void invalidate(boolean remove) {118if (remove) {119try {120inotifyRmWatch(ifd, wd);121} catch (UnixException x) {122// ignore123}124}125wd = -1;126}127128@Override129public boolean isValid() {130return (wd != -1);131}132133@Override134public void cancel() {135if (isValid()) {136// delegate to poller137((LinuxWatchService)watcher()).poller.cancel(this);138}139}140}141142/**143* Background thread to read from inotify144*/145private static class Poller extends AbstractPoller {146/**147* struct inotify_event {148* int wd;149* uint32_t mask;150* uint32_t len;151* char name __flexarr; // present if len > 0152* } act_t;153*/154private static final int SIZEOF_INOTIFY_EVENT = eventSize();155private static final int[] offsets = eventOffsets();156private static final int OFFSETOF_WD = offsets[0];157private static final int OFFSETOF_MASK = offsets[1];158private static final int OFFSETOF_LEN = offsets[3];159private static final int OFFSETOF_NAME = offsets[4];160161private static final int IN_MODIFY = 0x00000002;162private static final int IN_ATTRIB = 0x00000004;163private static final int IN_MOVED_FROM = 0x00000040;164private static final int IN_MOVED_TO = 0x00000080;165private static final int IN_CREATE = 0x00000100;166private static final int IN_DELETE = 0x00000200;167168private static final int IN_UNMOUNT = 0x00002000;169private static final int IN_Q_OVERFLOW = 0x00004000;170private static final int IN_IGNORED = 0x00008000;171172// sizeof buffer for when polling inotify173private static final int BUFFER_SIZE = 8192;174175private final UnixFileSystem fs;176private final LinuxWatchService watcher;177178// inotify file descriptor179private final int ifd;180// socketpair used to shutdown polling thread181private final int socketpair[];182// maps watch descriptor to Key183private final Map<Integer,LinuxWatchKey> wdToKey;184// address of read buffer185private final long address;186187Poller(UnixFileSystem fs, LinuxWatchService watcher, int ifd, int[] sp) {188this.fs = fs;189this.watcher = watcher;190this.ifd = ifd;191this.socketpair = sp;192this.wdToKey = new HashMap<Integer,LinuxWatchKey>();193this.address = unsafe.allocateMemory(BUFFER_SIZE);194}195196@Override197void wakeup() throws IOException {198// write to socketpair to wakeup polling thread199try {200write(socketpair[1], address, 1);201} catch (UnixException x) {202throw new IOException(x.errorString());203}204}205206@Override207Object implRegister(Path obj,208Set<? extends WatchEvent.Kind<?>> events,209WatchEvent.Modifier... modifiers)210{211UnixPath dir = (UnixPath)obj;212213int mask = 0;214for (WatchEvent.Kind<?> event: events) {215if (event == StandardWatchEventKinds.ENTRY_CREATE) {216mask |= IN_CREATE | IN_MOVED_TO;217continue;218}219if (event == StandardWatchEventKinds.ENTRY_DELETE) {220mask |= IN_DELETE | IN_MOVED_FROM;221continue;222}223if (event == StandardWatchEventKinds.ENTRY_MODIFY) {224mask |= IN_MODIFY | IN_ATTRIB;225continue;226}227}228229// no modifiers supported at this time230if (modifiers.length > 0) {231for (WatchEvent.Modifier modifier: modifiers) {232if (modifier == null)233return new NullPointerException();234if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)235continue; // ignore236return new UnsupportedOperationException("Modifier not supported");237}238}239240// check file is directory241UnixFileAttributes attrs = null;242try {243attrs = UnixFileAttributes.get(dir, true);244} catch (UnixException x) {245return x.asIOException(dir);246}247if (!attrs.isDirectory()) {248return new NotDirectoryException(dir.getPathForExceptionMessage());249}250251// register with inotify (replaces existing mask if already registered)252int wd = -1;253try {254NativeBuffer buffer =255NativeBuffers.asNativeBuffer(dir.getByteArrayForSysCalls());256try {257wd = inotifyAddWatch(ifd, buffer.address(), mask);258} finally {259buffer.release();260}261} catch (UnixException x) {262if (x.errno() == ENOSPC) {263return new IOException("User limit of inotify watches reached");264}265return x.asIOException(dir);266}267268// ensure watch descriptor is in map269LinuxWatchKey key = wdToKey.get(wd);270if (key == null) {271key = new LinuxWatchKey(dir, watcher, ifd, wd);272wdToKey.put(wd, key);273}274return key;275}276277// cancel single key278@Override279void implCancelKey(WatchKey obj) {280LinuxWatchKey key = (LinuxWatchKey)obj;281if (key.isValid()) {282wdToKey.remove(key.descriptor());283key.invalidate(true);284}285}286287// close watch service288@Override289void implCloseAll() {290// invalidate all keys291for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {292entry.getValue().invalidate(true);293}294wdToKey.clear();295296// free resources297unsafe.freeMemory(address);298UnixNativeDispatcher.close(socketpair[0]);299UnixNativeDispatcher.close(socketpair[1]);300UnixNativeDispatcher.close(ifd);301}302303/**304* Poller main loop305*/306@Override307public void run() {308try {309for (;;) {310int nReady, bytesRead;311312// wait for close or inotify event313nReady = poll(ifd, socketpair[0]);314315// read from inotify316try {317bytesRead = read(ifd, address, BUFFER_SIZE);318} catch (UnixException x) {319if (x.errno() != EAGAIN)320throw x;321bytesRead = 0;322}323324// iterate over buffer to decode events325int offset = 0;326while (offset < bytesRead) {327long event = address + offset;328int wd = unsafe.getInt(event + OFFSETOF_WD);329int mask = unsafe.getInt(event + OFFSETOF_MASK);330int len = unsafe.getInt(event + OFFSETOF_LEN);331332// file name333UnixPath name = null;334if (len > 0) {335int actual = len;336337// null-terminated and maybe additional null bytes to338// align the next event339while (actual > 0) {340long last = event + OFFSETOF_NAME + actual - 1;341if (unsafe.getByte(last) != 0)342break;343actual--;344}345if (actual > 0) {346byte[] buf = new byte[actual];347unsafe.copyMemory(null, event + OFFSETOF_NAME,348buf, Unsafe.ARRAY_BYTE_BASE_OFFSET, actual);349name = new UnixPath(fs, buf);350}351}352353// process event354processEvent(wd, mask, name);355356offset += (SIZEOF_INOTIFY_EVENT + len);357}358359// process any pending requests360if ((nReady > 1) || (nReady == 1 && bytesRead == 0)) {361try {362read(socketpair[0], address, BUFFER_SIZE);363boolean shutdown = processRequests();364if (shutdown)365break;366} catch (UnixException x) {367if (x.errno() != UnixConstants.EAGAIN)368throw x;369}370}371}372} catch (UnixException x) {373x.printStackTrace();374}375}376377378/**379* map inotify event to WatchEvent.Kind380*/381private WatchEvent.Kind<?> maskToEventKind(int mask) {382if ((mask & IN_MODIFY) > 0)383return StandardWatchEventKinds.ENTRY_MODIFY;384if ((mask & IN_ATTRIB) > 0)385return StandardWatchEventKinds.ENTRY_MODIFY;386if ((mask & IN_CREATE) > 0)387return StandardWatchEventKinds.ENTRY_CREATE;388if ((mask & IN_MOVED_TO) > 0)389return StandardWatchEventKinds.ENTRY_CREATE;390if ((mask & IN_DELETE) > 0)391return StandardWatchEventKinds.ENTRY_DELETE;392if ((mask & IN_MOVED_FROM) > 0)393return StandardWatchEventKinds.ENTRY_DELETE;394return null;395}396397/**398* Process event from inotify399*/400private void processEvent(int wd, int mask, final UnixPath name) {401// overflow - signal all keys402if ((mask & IN_Q_OVERFLOW) > 0) {403for (Map.Entry<Integer,LinuxWatchKey> entry: wdToKey.entrySet()) {404entry.getValue()405.signalEvent(StandardWatchEventKinds.OVERFLOW, null);406}407return;408}409410// lookup wd to get key411LinuxWatchKey key = wdToKey.get(wd);412if (key == null)413return; // should not happen414415// file deleted416if ((mask & IN_IGNORED) > 0) {417wdToKey.remove(wd);418key.invalidate(false);419key.signal();420return;421}422423// event for directory itself424if (name == null)425return;426427// map to event and queue to key428WatchEvent.Kind<?> kind = maskToEventKind(mask);429if (kind != null) {430key.signalEvent(kind, name);431}432}433}434435// -- native methods --436437// sizeof inotify_event438private static native int eventSize();439440// offsets of inotify_event441private static native int[] eventOffsets();442443private static native int inotifyInit() throws UnixException;444445private static native int inotifyAddWatch(int fd, long pathAddress, int mask)446throws UnixException;447448private static native void inotifyRmWatch(int fd, int wd)449throws UnixException;450451private static native void configureBlocking(int fd, boolean blocking)452throws UnixException;453454private static native void socketpair(int[] sv) throws UnixException;455456private static native int poll(int fd1, int fd2) throws UnixException;457458static {459AccessController.doPrivileged(new PrivilegedAction<Void>() {460public Void run() {461System.loadLibrary("nio");462return null;463}});464}465}466467468