jdk/src/solaris/classes/sun/nio/ch/EventPortWrapper.java
changeset 12872 16fa902b1469
child 20176 59b7a49e7f8b
equal deleted inserted replaced
12871:b583b4c82a82 12872:16fa902b1469
       
     1 /*
       
     2  * Copyright (c) 2012, Oracle and/or its affiliates. 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.nio.ch;
       
    27 
       
    28 import sun.misc.Unsafe;
       
    29 import java.io.IOException;
       
    30 import java.util.*;
       
    31 import static sun.nio.ch.SolarisEventPort.*;
       
    32 
       
    33 /**
       
    34  * Manages a Solaris event port and manipulates a native array of pollfd structs
       
    35  * on Solaris.
       
    36  */
       
    37 
       
    38 class EventPortWrapper {
       
    39     private static final Unsafe unsafe = Unsafe.getUnsafe();
       
    40     private static final int addressSize = unsafe.addressSize();
       
    41 
       
    42     // Maximum number of open file descriptors
       
    43     static final int   OPEN_MAX     = IOUtil.fdLimit();
       
    44 
       
    45     // Maximum number of events to retrive in one call to port_getn
       
    46     static final int   POLL_MAX     =  Math.min(OPEN_MAX-1, 1024);
       
    47 
       
    48     // initial size of the array to hold pending updates
       
    49     private final int INITIAL_PENDING_UPDATE_SIZE = 256;
       
    50 
       
    51     // maximum size of updateArray
       
    52     private final int MAX_UPDATE_ARRAY_SIZE = Math.min(OPEN_MAX, 64*1024);
       
    53 
       
    54     // special update status to indicate that it should be ignored
       
    55     private static final byte IGNORE = -1;
       
    56 
       
    57     // port file descriptor
       
    58     private final int pfd;
       
    59 
       
    60     // the poll array (populated by port_getn)
       
    61     private final long pollArrayAddress;
       
    62     private final AllocatedNativeObject pollArray;
       
    63 
       
    64     // required when accessing the update* fields
       
    65     private final Object updateLock = new Object();
       
    66 
       
    67     // the number of pending updates
       
    68     private int updateCount;
       
    69 
       
    70     // queue of file descriptors with updates pending
       
    71     private int[] updateDescriptors = new int[INITIAL_PENDING_UPDATE_SIZE];
       
    72 
       
    73     // events for file descriptors with registration changes pending, indexed
       
    74     // by file descriptor and stored as bytes for efficiency reasons. For
       
    75     // file descriptors higher than MAX_UPDATE_ARRAY_SIZE (unlimited case at
       
    76     // least then the update is stored in a map.
       
    77     private final byte[] eventsLow = new byte[MAX_UPDATE_ARRAY_SIZE];
       
    78     private Map<Integer,Byte> eventsHigh;
       
    79     // Used by release and updateRegistrations to track whether a file
       
    80     // descriptor is registered with /dev/poll.
       
    81     private final BitSet registered = new BitSet();
       
    82 
       
    83     // bit set to indicate if a file descriptor has been visited when
       
    84     // processing updates (used to avoid duplicates calls to port_associate)
       
    85     private BitSet visited = new BitSet();
       
    86 
       
    87     EventPortWrapper() throws IOException {
       
    88         int allocationSize = POLL_MAX * SIZEOF_PORT_EVENT;
       
    89         pollArray = new AllocatedNativeObject(allocationSize, true);
       
    90         pollArrayAddress = pollArray.address();
       
    91         this.pfd = port_create();
       
    92         if (OPEN_MAX > MAX_UPDATE_ARRAY_SIZE)
       
    93             eventsHigh = new HashMap<>();
       
    94     }
       
    95 
       
    96     void close() throws IOException {
       
    97         port_close(pfd);
       
    98         pollArray.free();
       
    99     }
       
   100 
       
   101     private short getSource(int i) {
       
   102         int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_SOURCE;
       
   103         return pollArray.getShort(offset);
       
   104     }
       
   105 
       
   106     int getEventOps(int i) {
       
   107         int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_EVENTS;
       
   108         return pollArray.getInt(offset);
       
   109     }
       
   110 
       
   111     int getDescriptor(int i) {
       
   112         int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT;
       
   113         if (addressSize == 4) {
       
   114             return pollArray.getInt(offset);
       
   115         } else {
       
   116             return (int) pollArray.getLong(offset);
       
   117         }
       
   118     }
       
   119 
       
   120     private void setDescriptor(int i, int fd) {
       
   121         int offset = SIZEOF_PORT_EVENT * i + OFFSETOF_OBJECT;
       
   122         if (addressSize == 4) {
       
   123             pollArray.putInt(offset, fd);
       
   124         } else {
       
   125             pollArray.putLong(offset, fd);
       
   126         }
       
   127     }
       
   128 
       
   129     private void setUpdate(int fd, byte events) {
       
   130         if (fd < MAX_UPDATE_ARRAY_SIZE) {
       
   131             eventsLow[fd] = events;
       
   132         } else {
       
   133             eventsHigh.put(Integer.valueOf(fd), Byte.valueOf(events));
       
   134         }
       
   135     }
       
   136 
       
   137     private byte getUpdate(int fd) {
       
   138         if (fd < MAX_UPDATE_ARRAY_SIZE) {
       
   139             return eventsLow[fd];
       
   140         } else {
       
   141             Byte result = eventsHigh.get(Integer.valueOf(fd));
       
   142             // result should never be null
       
   143             return result.byteValue();
       
   144         }
       
   145     }
       
   146 
       
   147     int poll(long timeout) throws IOException {
       
   148         // update registrations prior to poll
       
   149         synchronized (updateLock) {
       
   150 
       
   151             // process newest updates first
       
   152             int i = updateCount - 1;
       
   153             while (i >= 0) {
       
   154                 int fd = updateDescriptors[i];
       
   155                 if (!visited.get(fd)) {
       
   156                     short ev = getUpdate(fd);
       
   157                     if (ev != IGNORE) {
       
   158                         if (ev == 0) {
       
   159                             if (registered.get(fd)) {
       
   160                                 port_dissociate(pfd, PORT_SOURCE_FD, (long)fd);
       
   161                                 registered.clear(fd);
       
   162                             }
       
   163                         } else {
       
   164                             if (port_associate(pfd, PORT_SOURCE_FD, (long)fd, ev)) {
       
   165                                 registered.set(fd);
       
   166                             }
       
   167                         }
       
   168 
       
   169                     }
       
   170                     visited.set(fd);
       
   171                 }
       
   172                 i--;
       
   173             }
       
   174             updateCount = 0;
       
   175         }
       
   176 
       
   177         // poll for events
       
   178         int updated = port_getn(pfd, pollArrayAddress, POLL_MAX, timeout);
       
   179 
       
   180         // after polling we need to queue all polled file descriptors as they
       
   181         // are candidates to register for the next poll.
       
   182         synchronized (updateLock) {
       
   183             for (int i=0; i<updated; i++) {
       
   184                 if (getSource(i) == PORT_SOURCE_USER) {
       
   185                     interrupted = true;
       
   186                     setDescriptor(i, -1);
       
   187                 } else {
       
   188                     // the default is to re-associate for the next poll
       
   189                     int fd = getDescriptor(i);
       
   190                     registered.clear(fd);
       
   191                     setInterest(fd);
       
   192                 }
       
   193             }
       
   194         }
       
   195 
       
   196         return updated;
       
   197     }
       
   198 
       
   199     private void setInterest(int fd) {
       
   200         assert Thread.holdsLock(updateLock);
       
   201 
       
   202         // record the file descriptor and events, expanding the
       
   203         // respective arrays first if necessary.
       
   204         int oldCapacity = updateDescriptors.length;
       
   205         if (updateCount >= oldCapacity) {
       
   206             int newCapacity = oldCapacity + INITIAL_PENDING_UPDATE_SIZE;
       
   207             int[] newDescriptors = new int[newCapacity];
       
   208             System.arraycopy(updateDescriptors, 0, newDescriptors, 0, oldCapacity);
       
   209             updateDescriptors = newDescriptors;
       
   210         }
       
   211         updateDescriptors[updateCount++] = fd;
       
   212         visited.clear(fd);
       
   213     }
       
   214 
       
   215     void setInterest(int fd, int mask) {
       
   216         synchronized (updateLock) {
       
   217             setInterest(fd);
       
   218             setUpdate(fd, (byte)mask);
       
   219             assert getUpdate(fd) == mask;
       
   220         }
       
   221     }
       
   222 
       
   223     void release(int fd) {
       
   224         synchronized (updateLock) {
       
   225             if (registered.get(fd)) {
       
   226                 try {
       
   227                     port_dissociate(pfd, PORT_SOURCE_FD, (long)fd);
       
   228                 } catch (IOException ioe) {
       
   229                     throw new InternalError(ioe);
       
   230                 }
       
   231                 registered.clear(fd);
       
   232             }
       
   233             setUpdate(fd, IGNORE);
       
   234         }
       
   235     }
       
   236 
       
   237     // -- wakeup support --
       
   238 
       
   239     private boolean interrupted;
       
   240 
       
   241     public void interrupt() {
       
   242         try {
       
   243             port_send(pfd, 0);
       
   244         } catch (IOException ioe) {
       
   245             throw new InternalError(ioe);
       
   246         }
       
   247     }
       
   248 
       
   249     boolean interrupted() {
       
   250         return interrupted;
       
   251     }
       
   252 
       
   253     void clearInterrupted() {
       
   254         interrupted = false;
       
   255     }
       
   256 }