1 /*
   2  * Copyright (c) 2011, 2013, 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 /*
  27  * KQueueArrayWrapper.java
  28  * Implementation of Selector using FreeBSD / Mac OS X kqueues
  29  * Derived from Sun's DevPollArrayWrapper
  30  */
  31 
  32 package sun.nio.ch;
  33 
  34 import java.io.IOException;
  35 import java.io.FileDescriptor;
  36 import java.util.Iterator;
  37 import java.util.LinkedList;
  38 
  39 /*
  40  * struct kevent {           // 32-bit    64-bit
  41  *     uintptr_t ident;      //   4         8
  42  *     short     filter;     //   2         2
  43  *     u_short   flags;      //   2         2
  44  *     u_int     fflags;     //   4         4
  45  *     intptr_t  data;       //   4         8
  46  *     void      *udata;     //   4         8
  47  * }                  // Total:  20        32
  48  *
  49  * The implementation works in 32-bit and 64-bit world. We do this by calling a
  50  * native function that actually sets the sizes and offsets of the fields based
  51  * on which mode we're in.
  52  */
  53 
  54 class KQueueArrayWrapper {
  55     // kevent filters
  56     static short EVFILT_READ;
  57     static short EVFILT_WRITE;
  58 
  59     // kevent struct
  60     // These fields are now set by initStructSizes in the static initializer.
  61     static short SIZEOF_KEVENT;
  62     static short FD_OFFSET;
  63     static short FILTER_OFFSET;
  64 
  65     // kevent array size
  66     static final int NUM_KEVENTS = 128;
  67 
  68     // Are we in a 64-bit VM?
  69     static boolean is64bit = false;
  70 
  71     // The kevent array (used for outcoming events only)
  72     private AllocatedNativeObject keventArray = null;
  73     private long keventArrayAddress;
  74 
  75     // The kqueue fd
  76     private int kq = -1;
  77 
  78     // The fd of the interrupt line going out
  79     private int outgoingInterruptFD;
  80 
  81     // The fd of the interrupt line coming in
  82     private int incomingInterruptFD;
  83 
  84     static {
  85         IOUtil.load();
  86         initStructSizes();
  87         String datamodel = java.security.AccessController.doPrivileged(
  88             new sun.security.action.GetPropertyAction("sun.arch.data.model")
  89         );
  90         is64bit = datamodel.equals("64");
  91     }
  92 
  93     KQueueArrayWrapper() {
  94         int allocationSize = SIZEOF_KEVENT * NUM_KEVENTS;
  95         keventArray = new AllocatedNativeObject(allocationSize, true);
  96         keventArrayAddress = keventArray.address();
  97         kq = init();
  98     }
  99 
 100     // Used to update file description registrations
 101     private static class Update {
 102         SelChImpl channel;
 103         int events;
 104         Update(SelChImpl channel, int events) {
 105             this.channel = channel;
 106             this.events = events;
 107         }
 108     }
 109 
 110     private LinkedList<Update> updateList = new LinkedList<Update>();
 111 
 112     void initInterrupt(int fd0, int fd1) {
 113         outgoingInterruptFD = fd1;
 114         incomingInterruptFD = fd0;
 115         register0(kq, fd0, 1, 0);
 116     }
 117 
 118     int getReventOps(int index) {
 119         int result = 0;
 120         int offset = SIZEOF_KEVENT*index + FILTER_OFFSET;
 121         short filter = keventArray.getShort(offset);
 122 
 123         // This is all that's necessary based on inspection of usage:
 124         //   SinkChannelImpl, SourceChannelImpl, DatagramChannelImpl,
 125         //   ServerSocketChannelImpl, SocketChannelImpl
 126         if (filter == EVFILT_READ) {
 127             result |= Net.POLLIN;
 128         } else if (filter == EVFILT_WRITE) {
 129             result |= Net.POLLOUT;
 130         }
 131 
 132         return result;
 133     }
 134 
 135     int getDescriptor(int index) {
 136         int offset = SIZEOF_KEVENT*index + FD_OFFSET;
 137         /* The ident field is 8 bytes in 64-bit world, however the API wants us
 138          * to return an int. Hence read the 8 bytes but return as an int.
 139          */
 140         if (is64bit) {
 141           long fd = keventArray.getLong(offset);
 142           assert fd <= Integer.MAX_VALUE;
 143           return (int) fd;
 144         } else {
 145           return keventArray.getInt(offset);
 146         }
 147     }
 148 
 149     void setInterest(SelChImpl channel, int events) {
 150         synchronized (updateList) {
 151             // update existing registration
 152             updateList.add(new Update(channel, events));
 153         }
 154     }
 155 
 156     void release(SelChImpl channel) {
 157         synchronized (updateList) {
 158             // flush any pending updates
 159             for (Iterator<Update> it = updateList.iterator(); it.hasNext();) {
 160                 if (it.next().channel == channel) {
 161                     it.remove();
 162                 }
 163             }
 164 
 165             // remove
 166             register0(kq, channel.getFDVal(), 0, 0);
 167         }
 168     }
 169 
 170     void updateRegistrations() {
 171         synchronized (updateList) {
 172             Update u = null;
 173             while ((u = updateList.poll()) != null) {
 174                 SelChImpl ch = u.channel;
 175                 if (!ch.isOpen())
 176                     continue;
 177 
 178                 register0(kq, ch.getFDVal(), u.events & Net.POLLIN, u.events & Net.POLLOUT);
 179             }
 180         }
 181     }
 182 
 183 
 184     void close() throws IOException {
 185         if (keventArray != null) {
 186             keventArray.free();
 187             keventArray = null;
 188         }
 189         if (kq >= 0) {
 190             FileDispatcherImpl.closeIntFD(kq);
 191             kq = -1;
 192         }
 193     }
 194 
 195     int poll(long timeout) {
 196         updateRegistrations();
 197         int updated = kevent0(kq, keventArrayAddress, NUM_KEVENTS, timeout);
 198         return updated;
 199     }
 200 
 201     void interrupt() {
 202         interrupt(outgoingInterruptFD);
 203     }
 204 
 205     private native int init();
 206     private static native void initStructSizes();
 207 
 208     private native void register0(int kq, int fd, int read, int write);
 209     private native int kevent0(int kq, long keventAddress, int keventCount,
 210                                long timeout);
 211     private static native void interrupt(int fd);
 212 }