jdk/src/solaris/classes/sun/nio/fs/SolarisWatchService.java
changeset 2057 3acf8e5e2ca0
child 5506 202f599c92aa
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.nio.fs;
       
    27 
       
    28 import java.nio.file.*;
       
    29 import java.security.AccessController;
       
    30 import java.security.PrivilegedAction;
       
    31 import java.util.*;
       
    32 import java.io.IOException;
       
    33 import sun.misc.Unsafe;
       
    34 
       
    35 import static sun.nio.fs.UnixConstants.*;
       
    36 
       
    37 /**
       
    38  * Solaris implementation of WatchService based on file events notification
       
    39  * facility.
       
    40  */
       
    41 
       
    42 class SolarisWatchService
       
    43     extends AbstractWatchService
       
    44 {
       
    45     private static final Unsafe unsafe = Unsafe.getUnsafe();
       
    46     private static int addressSize = unsafe.addressSize();
       
    47 
       
    48     private static int dependsArch(int value32, int value64) {
       
    49         return (addressSize == 4) ? value32 : value64;
       
    50     }
       
    51 
       
    52     /*
       
    53      * typedef struct port_event {
       
    54      *     int             portev_events;
       
    55      *     ushort_t        portev_source;
       
    56      *     ushort_t        portev_pad;
       
    57      *     uintptr_t       portev_object;
       
    58      *     void            *portev_user;
       
    59      * } port_event_t;
       
    60      */
       
    61     private static final int SIZEOF_PORT_EVENT  = dependsArch(16, 24);
       
    62     private static final int OFFSETOF_EVENTS    = 0;
       
    63     private static final int OFFSETOF_SOURCE    = 4;
       
    64     private static final int OFFSETOF_OBJECT    = 8;
       
    65 
       
    66     /*
       
    67      * typedef struct file_obj {
       
    68      *     timestruc_t     fo_atime;
       
    69      *     timestruc_t     fo_mtime;
       
    70      *     timestruc_t     fo_ctime;
       
    71      *     uintptr_t       fo_pad[3];
       
    72      *     char            *fo_name;
       
    73      * } file_obj_t;
       
    74      */
       
    75     private static final int SIZEOF_FILEOBJ    = dependsArch(40, 80);
       
    76     private static final int OFFSET_FO_NAME    = dependsArch(36, 72);
       
    77 
       
    78     // port sources
       
    79     private static final short PORT_SOURCE_USER     = 3;
       
    80     private static final short PORT_SOURCE_FILE     = 7;
       
    81 
       
    82     // user-watchable events
       
    83     private static final int FILE_MODIFIED      = 0x00000002;
       
    84     private static final int FILE_ATTRIB        = 0x00000004;
       
    85     private static final int FILE_NOFOLLOW      = 0x10000000;
       
    86 
       
    87     // exception events
       
    88     private static final int FILE_DELETE        = 0x00000010;
       
    89     private static final int FILE_RENAME_TO     = 0x00000020;
       
    90     private static final int FILE_RENAME_FROM   = 0x00000040;
       
    91     private static final int UNMOUNTED          = 0x20000000;
       
    92     private static final int MOUNTEDOVER        = 0x40000000;
       
    93 
       
    94     // background thread to read change events
       
    95     private final Poller poller;
       
    96 
       
    97     SolarisWatchService(UnixFileSystem fs) throws IOException {
       
    98         int port = -1;
       
    99         try {
       
   100             port = portCreate();
       
   101         } catch (UnixException x) {
       
   102             throw new IOException(x.errorString());
       
   103         }
       
   104 
       
   105         this.poller = new Poller(fs, this, port);
       
   106         this.poller.start();
       
   107     }
       
   108 
       
   109     @Override
       
   110     WatchKey register(Path dir,
       
   111                       WatchEvent.Kind<?>[] events,
       
   112                       WatchEvent.Modifier... modifiers)
       
   113          throws IOException
       
   114     {
       
   115         // delegate to poller
       
   116         return poller.register(dir, events, modifiers);
       
   117     }
       
   118 
       
   119     @Override
       
   120     void implClose() throws IOException {
       
   121         // delegate to poller
       
   122         poller.close();
       
   123     }
       
   124 
       
   125     /**
       
   126      * WatchKey implementation
       
   127      */
       
   128     private class SolarisWatchKey extends AbstractWatchKey
       
   129         implements DirectoryNode
       
   130     {
       
   131         private final UnixPath dir;
       
   132         private final UnixFileKey fileKey;
       
   133 
       
   134         // pointer to native file_obj object
       
   135         private final long object;
       
   136 
       
   137         // events (may be changed). set to null when watch key is invalid
       
   138         private volatile Set<? extends WatchEvent.Kind<?>> events;
       
   139 
       
   140         // map of entries in directory; created lazily; accessed only by
       
   141         // poller thread.
       
   142         private Map<Path,EntryNode> children;
       
   143 
       
   144         SolarisWatchKey(SolarisWatchService watcher,
       
   145                         UnixPath dir,
       
   146                         UnixFileKey fileKey,
       
   147                         long object,
       
   148                         Set<? extends WatchEvent.Kind<?>> events)
       
   149         {
       
   150             super(watcher);
       
   151             this.dir = dir;
       
   152             this.fileKey = fileKey;
       
   153             this.object = object;
       
   154             this.events = events;
       
   155         }
       
   156 
       
   157         UnixPath getFileRef() {
       
   158             return dir;
       
   159         }
       
   160 
       
   161         UnixFileKey getFileKey() {
       
   162             return fileKey;
       
   163         }
       
   164 
       
   165         @Override
       
   166         public long object() {
       
   167             return object;
       
   168         }
       
   169 
       
   170         void invalidate() {
       
   171             events = null;
       
   172         }
       
   173 
       
   174         Set<? extends WatchEvent.Kind<?>> events() {
       
   175             return events;
       
   176         }
       
   177 
       
   178         void setEvents(Set<? extends WatchEvent.Kind<?>> events) {
       
   179             this.events = events;
       
   180         }
       
   181 
       
   182         @Override
       
   183         public boolean isValid() {
       
   184             return events != null;
       
   185         }
       
   186 
       
   187         @Override
       
   188         public void cancel() {
       
   189             if (isValid()) {
       
   190                 // delegate to poller
       
   191                 poller.cancel(this);
       
   192             }
       
   193         }
       
   194 
       
   195         @Override
       
   196         public void addChild(Path name, EntryNode node) {
       
   197             if (children == null)
       
   198                 children = new HashMap<Path,EntryNode>();
       
   199             children.put(name, node);
       
   200         }
       
   201 
       
   202         @Override
       
   203         public void removeChild(Path name) {
       
   204             children.remove(name);
       
   205         }
       
   206 
       
   207         @Override
       
   208         public EntryNode getChild(Path name) {
       
   209             if (children != null)
       
   210                 return children.get(name);
       
   211             return null;
       
   212         }
       
   213     }
       
   214 
       
   215     /**
       
   216      * Background thread to read from port
       
   217      */
       
   218     private class Poller extends AbstractPoller {
       
   219 
       
   220         // maximum number of events to read per call to port_getn
       
   221         private static final int MAX_EVENT_COUNT            = 128;
       
   222 
       
   223         // events that map to ENTRY_DELETE
       
   224         private static final int FILE_REMOVED =
       
   225             (FILE_DELETE|FILE_RENAME_TO|FILE_RENAME_FROM);
       
   226 
       
   227         // events that tell us not to re-associate the object
       
   228         private static final int FILE_EXCEPTION =
       
   229             (FILE_REMOVED|UNMOUNTED|MOUNTEDOVER);
       
   230 
       
   231         // address of event buffers (used to receive events with port_getn)
       
   232         private final long bufferAddress;
       
   233 
       
   234         private final SolarisWatchService watcher;
       
   235 
       
   236         // the I/O port
       
   237         private final int port;
       
   238 
       
   239         // maps file key (dev/inode) to WatchKey
       
   240         private final Map<UnixFileKey,SolarisWatchKey> fileKey2WatchKey;
       
   241 
       
   242         // maps file_obj object to Node
       
   243         private final Map<Long,Node> object2Node;
       
   244 
       
   245         /**
       
   246          * Create a new instance
       
   247          */
       
   248         Poller(UnixFileSystem fs, SolarisWatchService watcher, int port) {
       
   249             this.watcher = watcher;
       
   250             this.port = port;
       
   251             this.bufferAddress =
       
   252                 unsafe.allocateMemory(SIZEOF_PORT_EVENT * MAX_EVENT_COUNT);
       
   253             this.fileKey2WatchKey = new HashMap<UnixFileKey,SolarisWatchKey>();
       
   254             this.object2Node = new HashMap<Long,Node>();
       
   255         }
       
   256 
       
   257         @Override
       
   258         void wakeup() throws IOException {
       
   259             // write to port to wakeup polling thread
       
   260             try {
       
   261                 portSend(port, 0);
       
   262             } catch (UnixException x) {
       
   263                 throw new IOException(x.errorString());
       
   264             }
       
   265         }
       
   266 
       
   267         @Override
       
   268         Object implRegister(Path obj,
       
   269                             Set<? extends WatchEvent.Kind<?>> events,
       
   270                             WatchEvent.Modifier... modifiers)
       
   271         {
       
   272             // no modifiers supported at this time
       
   273             if (modifiers.length > 0) {
       
   274                 for (WatchEvent.Modifier modifier: modifiers) {
       
   275                     if (modifier == null)
       
   276                         return new NullPointerException();
       
   277                     if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier)
       
   278                         continue; // ignore
       
   279                     return new UnsupportedOperationException("Modifier not supported");
       
   280                 }
       
   281             }
       
   282 
       
   283             UnixPath dir = (UnixPath)obj;
       
   284 
       
   285             // check file is directory
       
   286             UnixFileAttributes attrs = null;
       
   287             try {
       
   288                 attrs = UnixFileAttributes.get(dir, true);
       
   289             } catch (UnixException x) {
       
   290                 return x.asIOException(dir);
       
   291             }
       
   292             if (!attrs.isDirectory()) {
       
   293                 return new NotDirectoryException(dir.getPathForExecptionMessage());
       
   294             }
       
   295 
       
   296             // return existing watch key after updating events if already
       
   297             // registered
       
   298             UnixFileKey fileKey = attrs.fileKey();
       
   299             SolarisWatchKey watchKey = fileKey2WatchKey.get(fileKey);
       
   300             if (watchKey != null) {
       
   301                 updateEvents(watchKey, events);
       
   302                 return watchKey;
       
   303             }
       
   304 
       
   305             // register directory
       
   306             long object = 0L;
       
   307             try {
       
   308                 object = registerImpl(dir, (FILE_MODIFIED | FILE_ATTRIB));
       
   309             } catch (UnixException x) {
       
   310                 return x.asIOException(dir);
       
   311             }
       
   312 
       
   313             // create watch key and insert it into maps
       
   314             watchKey = new SolarisWatchKey(watcher, dir, fileKey, object, events);
       
   315             object2Node.put(object, watchKey);
       
   316             fileKey2WatchKey.put(fileKey, watchKey);
       
   317 
       
   318             // register all entries in directory
       
   319             registerChildren(dir, watchKey, false);
       
   320 
       
   321             return watchKey;
       
   322         }
       
   323 
       
   324         // cancel single key
       
   325         @Override
       
   326         void implCancelKey(WatchKey obj) {
       
   327            SolarisWatchKey key = (SolarisWatchKey)obj;
       
   328            if (key.isValid()) {
       
   329                fileKey2WatchKey.remove(key.getFileKey());
       
   330 
       
   331                // release resources for entries in directory
       
   332                if (key.children != null) {
       
   333                    for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
       
   334                        EntryNode node = entry.getValue();
       
   335                        long object = node.object();
       
   336                        object2Node.remove(object);
       
   337                        releaseObject(object, true);
       
   338                    }
       
   339                }
       
   340 
       
   341                // release resources for directory
       
   342                long object = key.object();
       
   343                object2Node.remove(object);
       
   344                releaseObject(object, true);
       
   345 
       
   346                // and finally invalidate the key
       
   347                key.invalidate();
       
   348            }
       
   349         }
       
   350 
       
   351         // close watch service
       
   352         @Override
       
   353         void implCloseAll() {
       
   354             // release all native resources
       
   355             for (Long object: object2Node.keySet()) {
       
   356                 releaseObject(object, true);
       
   357             }
       
   358 
       
   359             // invalidate all keys
       
   360             for (Map.Entry<UnixFileKey,SolarisWatchKey> entry: fileKey2WatchKey.entrySet()) {
       
   361                 entry.getValue().invalidate();
       
   362             }
       
   363 
       
   364             // clean-up
       
   365             object2Node.clear();
       
   366             fileKey2WatchKey.clear();
       
   367 
       
   368             // free global resources
       
   369             unsafe.freeMemory(bufferAddress);
       
   370             UnixNativeDispatcher.close(port);
       
   371         }
       
   372 
       
   373         /**
       
   374          * Poller main loop. Blocks on port_getn waiting for events and then
       
   375          * processes them.
       
   376          */
       
   377         @Override
       
   378         public void run() {
       
   379             try {
       
   380                 for (;;) {
       
   381                     int n = portGetn(port, bufferAddress, MAX_EVENT_COUNT);
       
   382                     assert n > 0;
       
   383 
       
   384                     long address = bufferAddress;
       
   385                     for (int i=0; i<n; i++) {
       
   386                         boolean shutdown = processEvent(address);
       
   387                         if (shutdown)
       
   388                             return;
       
   389                         address += SIZEOF_PORT_EVENT;
       
   390                     }
       
   391                 }
       
   392             } catch (UnixException x) {
       
   393                 x.printStackTrace();
       
   394             }
       
   395         }
       
   396 
       
   397         /**
       
   398          * Process a single port_event
       
   399          *
       
   400          * Returns true if poller thread is requested to shutdown.
       
   401          */
       
   402         boolean processEvent(long address) {
       
   403             // pe->portev_source
       
   404             short source = unsafe.getShort(address + OFFSETOF_SOURCE);
       
   405             // pe->portev_object
       
   406             long object = unsafe.getAddress(address + OFFSETOF_OBJECT);
       
   407             // pe->portev_events
       
   408             int events = unsafe.getInt(address + OFFSETOF_EVENTS);
       
   409 
       
   410             // user event is trigger to process pending requests
       
   411             if (source != PORT_SOURCE_FILE) {
       
   412                 if (source == PORT_SOURCE_USER) {
       
   413                     // process any pending requests
       
   414                     boolean shutdown = processRequests();
       
   415                     if (shutdown)
       
   416                         return true;
       
   417                 }
       
   418                 return false;
       
   419             }
       
   420 
       
   421             // lookup object to get Node
       
   422             Node node = object2Node.get(object);
       
   423             if (node == null) {
       
   424                 // should not happen
       
   425                 return false;
       
   426             }
       
   427 
       
   428             // As a workaround for 6642290 and 6636438/6636412 we don't use
       
   429             // FILE_EXCEPTION events to tell use not to register the file.
       
   430             // boolean reregister = (events & FILE_EXCEPTION) == 0;
       
   431             boolean reregister = true;
       
   432 
       
   433             // If node is EntryNode then event relates to entry in directory
       
   434             // If node is a SolarisWatchKey (DirectoryNode) then event relates
       
   435             // to a watched directory.
       
   436             boolean isDirectory = (node instanceof SolarisWatchKey);
       
   437             if (isDirectory) {
       
   438                 processDirectoryEvents((SolarisWatchKey)node, events);
       
   439             } else {
       
   440                 boolean ignore = processEntryEvents((EntryNode)node, events);
       
   441                 if (ignore)
       
   442                     reregister = false;
       
   443             }
       
   444 
       
   445             // need to re-associate to get further events
       
   446             if (reregister) {
       
   447                 try {
       
   448                     events = FILE_MODIFIED | FILE_ATTRIB;
       
   449                     if (!isDirectory) events |= FILE_NOFOLLOW;
       
   450                     portAssociate(port,
       
   451                                   PORT_SOURCE_FILE,
       
   452                                   object,
       
   453                                   events);
       
   454                 } catch (UnixException x) {
       
   455                     // unable to re-register
       
   456                     reregister = false;
       
   457                 }
       
   458             }
       
   459 
       
   460             // object is not re-registered so release resources. If
       
   461             // object is a watched directory then signal key
       
   462             if (!reregister) {
       
   463                 // release resources
       
   464                 object2Node.remove(object);
       
   465                 releaseObject(object, false);
       
   466 
       
   467                 // if watch key then signal it
       
   468                 if (isDirectory) {
       
   469                     SolarisWatchKey key = (SolarisWatchKey)node;
       
   470                     fileKey2WatchKey.remove( key.getFileKey() );
       
   471                     key.invalidate();
       
   472                     key.signal();
       
   473                 } else {
       
   474                     // if entry then remove it from parent
       
   475                     EntryNode entry = (EntryNode)node;
       
   476                     SolarisWatchKey key = (SolarisWatchKey)entry.parent();
       
   477                     key.removeChild(entry.name());
       
   478                 }
       
   479             }
       
   480 
       
   481             return false;
       
   482         }
       
   483 
       
   484         /**
       
   485          * Process directory events. If directory is modified then re-scan
       
   486          * directory to register any new entries
       
   487          */
       
   488         void processDirectoryEvents(SolarisWatchKey key, int mask) {
       
   489             if ((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) {
       
   490                 registerChildren(key.getFileRef(), key,
       
   491                     key.events().contains(StandardWatchEventKind.ENTRY_CREATE));
       
   492             }
       
   493         }
       
   494 
       
   495         /**
       
   496          * Process events for entries in registered directories. Returns {@code
       
   497          * true} if events are ignored because the watch key has been cancelled.
       
   498          */
       
   499         boolean processEntryEvents(EntryNode node, int mask) {
       
   500             SolarisWatchKey key = (SolarisWatchKey)node.parent();
       
   501             Set<? extends WatchEvent.Kind<?>> events = key.events();
       
   502             if (events == null) {
       
   503                 // key has been cancelled so ignore event
       
   504                 return true;
       
   505             }
       
   506 
       
   507             // entry modified
       
   508             if (((mask & (FILE_MODIFIED | FILE_ATTRIB)) != 0) &&
       
   509                 events.contains(StandardWatchEventKind.ENTRY_MODIFY))
       
   510             {
       
   511                 key.signalEvent(StandardWatchEventKind.ENTRY_MODIFY, node.name());
       
   512             }
       
   513 
       
   514             // entry removed
       
   515             if (((mask & (FILE_REMOVED)) != 0) &&
       
   516                 events.contains(StandardWatchEventKind.ENTRY_DELETE))
       
   517             {
       
   518                 // Due to 6636438/6636412 we may get a remove event for cases
       
   519                 // where a rmdir/unlink/rename is attempted but fails. Until
       
   520                 // this issue is resolved we re-lstat the file to check if it
       
   521                 // exists. If it exists then we ignore the event. To keep the
       
   522                 // workaround simple we don't check the st_ino so it isn't
       
   523                 // effective when the file is replaced.
       
   524                 boolean removed = true;
       
   525                 try {
       
   526                     UnixFileAttributes
       
   527                         .get(key.getFileRef().resolve(node.name()), false);
       
   528                     removed = false;
       
   529                 } catch (UnixException x) { }
       
   530 
       
   531                 if (removed)
       
   532                     key.signalEvent(StandardWatchEventKind.ENTRY_DELETE, node.name());
       
   533             }
       
   534             return false;
       
   535         }
       
   536 
       
   537         /**
       
   538          * Registers all entries in the given directory
       
   539          *
       
   540          * The {@code sendEvents} parameter indicates if ENTRY_CREATE events
       
   541          * should be queued when new entries are found. When initially
       
   542          * registering a directory then will always be false. When re-scanning
       
   543          * a directory then it depends on if the event is enabled or not.
       
   544          */
       
   545         void registerChildren(UnixPath dir,
       
   546                               SolarisWatchKey parent,
       
   547                               boolean sendEvents)
       
   548         {
       
   549             // if the ENTRY_MODIFY event is not enabled then we don't need
       
   550             // modification events for entries in the directory
       
   551             int events = FILE_NOFOLLOW;
       
   552             if (parent.events().contains(StandardWatchEventKind.ENTRY_MODIFY))
       
   553                 events |= (FILE_MODIFIED | FILE_ATTRIB);
       
   554 
       
   555             DirectoryStream<Path> stream = null;
       
   556             try {
       
   557                 stream = dir.newDirectoryStream();
       
   558             } catch (IOException x) {
       
   559                 // nothing we can do
       
   560                 return;
       
   561             }
       
   562             try {
       
   563                 for (Path entry: stream) {
       
   564                     Path name = entry.getName();
       
   565 
       
   566                     // skip entry if already registered
       
   567                     if (parent.getChild(name) != null)
       
   568                         continue;
       
   569 
       
   570                     // send ENTRY_CREATE if enabled
       
   571                     if (sendEvents) {
       
   572                         parent.signalEvent(StandardWatchEventKind.ENTRY_CREATE, name);
       
   573                     }
       
   574 
       
   575                     // register it
       
   576                     long object = 0L;
       
   577                     try {
       
   578                         object = registerImpl((UnixPath)entry, events);
       
   579                     } catch (UnixException x) {
       
   580                         // can't register so ignore for now.
       
   581                         continue;
       
   582                     }
       
   583 
       
   584                     // create node
       
   585                     EntryNode node = new EntryNode(object, entry.getName(), parent);
       
   586                     // tell the parent about it
       
   587                     parent.addChild(entry.getName(), node);
       
   588                     object2Node.put(object, node);
       
   589                 }
       
   590             } catch (ConcurrentModificationException x) {
       
   591                 // error during iteration which we ignore for now
       
   592             } finally {
       
   593                 try {
       
   594                     stream.close();
       
   595                 } catch (IOException x) { }
       
   596             }
       
   597         }
       
   598 
       
   599         /**
       
   600          * Update watch key's events. Where the ENTRY_MODIFY changes then we
       
   601          * need to update the events of registered children.
       
   602          */
       
   603         void updateEvents(SolarisWatchKey key, Set<? extends WatchEvent.Kind<?>> events) {
       
   604             // update events, rembering if ENTRY_MODIFY was previously
       
   605             // enabled or disabled.
       
   606             boolean wasModifyEnabled = key.events()
       
   607                 .contains(StandardWatchEventKind.ENTRY_MODIFY);
       
   608             key.setEvents(events);
       
   609 
       
   610             // check if ENTRY_MODIFY has changed
       
   611             boolean isModifyEnabled = events
       
   612                 .contains(StandardWatchEventKind.ENTRY_MODIFY);
       
   613             if (wasModifyEnabled == isModifyEnabled) {
       
   614                 return;
       
   615             }
       
   616 
       
   617             // if changed then update events of children
       
   618             if (key.children != null) {
       
   619                 int ev = FILE_NOFOLLOW;
       
   620                 if (isModifyEnabled)
       
   621                     ev |= (FILE_MODIFIED | FILE_ATTRIB);
       
   622 
       
   623                 for (Map.Entry<Path,EntryNode> entry: key.children.entrySet()) {
       
   624                     long object = entry.getValue().object();
       
   625                     try {
       
   626                         portAssociate(port,
       
   627                                       PORT_SOURCE_FILE,
       
   628                                       object,
       
   629                                       ev);
       
   630                     } catch (UnixException x) {
       
   631                         // nothing we can do.
       
   632                     }
       
   633                 }
       
   634             }
       
   635         }
       
   636 
       
   637         /**
       
   638          * Calls port_associate to register the given path.
       
   639          * Returns pointer to fileobj structure that is allocated for
       
   640          * the registration.
       
   641          */
       
   642         long registerImpl(UnixPath dir, int events)
       
   643             throws UnixException
       
   644         {
       
   645             // allocate memory for the path (file_obj->fo_name field)
       
   646             byte[] path = dir.getByteArrayForSysCalls();
       
   647             int len = path.length;
       
   648             long name = unsafe.allocateMemory(len+1);
       
   649             unsafe.copyMemory(path, Unsafe.ARRAY_BYTE_BASE_OFFSET, null,
       
   650                 name, (long)len);
       
   651             unsafe.putByte(name + len, (byte)0);
       
   652 
       
   653             // allocate memory for filedatanode structure - this is the object
       
   654             // to port_associate
       
   655             long object = unsafe.allocateMemory(SIZEOF_FILEOBJ);
       
   656             unsafe.setMemory(null, object, SIZEOF_FILEOBJ, (byte)0);
       
   657             unsafe.putAddress(object + OFFSET_FO_NAME, name);
       
   658 
       
   659             // associate the object with the port
       
   660             try {
       
   661                 portAssociate(port,
       
   662                               PORT_SOURCE_FILE,
       
   663                               object,
       
   664                               events);
       
   665             } catch (UnixException x) {
       
   666                 // debugging
       
   667                 if (x.errno() == EAGAIN) {
       
   668                     System.err.println("The maximum number of objects associated "+
       
   669                         "with the port has been reached");
       
   670                 }
       
   671 
       
   672                 unsafe.freeMemory(name);
       
   673                 unsafe.freeMemory(object);
       
   674                 throw x;
       
   675             }
       
   676             return object;
       
   677         }
       
   678 
       
   679         /**
       
   680          * Frees all resources for an file_obj object; optionally remove
       
   681          * association from port
       
   682          */
       
   683         void releaseObject(long object, boolean dissociate) {
       
   684             // remove association
       
   685             if (dissociate) {
       
   686                 try {
       
   687                     portDissociate(port, PORT_SOURCE_FILE, object);
       
   688                 } catch (UnixException x) {
       
   689                     // ignore
       
   690                 }
       
   691             }
       
   692 
       
   693             // free native memory
       
   694             long name = unsafe.getAddress(object + OFFSET_FO_NAME);
       
   695             unsafe.freeMemory(name);
       
   696             unsafe.freeMemory(object);
       
   697         }
       
   698     }
       
   699 
       
   700     /**
       
   701      * A node with native (file_obj) resources
       
   702      */
       
   703     private static interface Node {
       
   704         long object();
       
   705     }
       
   706 
       
   707     /**
       
   708      * A directory node with a map of the entries in the directory
       
   709      */
       
   710     private static interface DirectoryNode extends Node {
       
   711         void addChild(Path name, EntryNode node);
       
   712         void removeChild(Path name);
       
   713         EntryNode getChild(Path name);
       
   714     }
       
   715 
       
   716     /**
       
   717      * An implementation of a node that is an entry in a directory.
       
   718      */
       
   719     private static class EntryNode implements Node {
       
   720         private final long object;
       
   721         private final Path name;
       
   722         private final DirectoryNode parent;
       
   723 
       
   724         EntryNode(long object, Path name, DirectoryNode parent) {
       
   725             this.object = object;
       
   726             this.name = name;
       
   727             this.parent = parent;
       
   728         }
       
   729 
       
   730         @Override
       
   731         public long object() {
       
   732             return object;
       
   733         }
       
   734 
       
   735         Path name() {
       
   736             return name;
       
   737         }
       
   738 
       
   739         DirectoryNode parent() {
       
   740             return parent;
       
   741         }
       
   742     }
       
   743 
       
   744     // -- native methods --
       
   745 
       
   746     private static native void init();
       
   747 
       
   748     private static native int portCreate() throws UnixException;
       
   749 
       
   750     private static native void portAssociate(int port, int source, long object, int events)
       
   751         throws UnixException;
       
   752 
       
   753     private static native void portDissociate(int port, int source, long object)
       
   754         throws UnixException;
       
   755 
       
   756     private static native void portSend(int port, int events)
       
   757         throws UnixException;
       
   758 
       
   759     private static native int portGetn(int port, long address, int max)
       
   760         throws UnixException;
       
   761 
       
   762     static {
       
   763         AccessController.doPrivileged(new PrivilegedAction<Void>() {
       
   764             public Void run() {
       
   765                 System.loadLibrary("nio");
       
   766                 return null;
       
   767         }});
       
   768         init();
       
   769     }
       
   770 }