Source for gnu.java.nio.EpollSelectorImpl

   1: /* EpollSelectorImpl.java -- selector implementation using epoll
   2:    Copyright (C) 2006 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.nio;
  40: 
  41: import gnu.classpath.Configuration;
  42: 
  43: import java.io.IOException;
  44: import java.nio.ByteBuffer;
  45: import java.nio.channels.SelectableChannel;
  46: import java.nio.channels.SelectionKey;
  47: import java.nio.channels.Selector;
  48: import java.nio.channels.spi.AbstractSelectableChannel;
  49: import java.nio.channels.spi.AbstractSelector;
  50: import java.nio.channels.spi.SelectorProvider;
  51: import java.util.Collections;
  52: import java.util.HashMap;
  53: import java.util.HashSet;
  54: import java.util.Iterator;
  55: import java.util.Set;
  56: 
  57: /**
  58:  * An implementation of {@link Selector} that uses the epoll event
  59:  * notification mechanism on GNU/Linux.
  60:  *
  61:  * @author Casey Marshall (csm@gnu.org)
  62:  */
  63: public class EpollSelectorImpl extends AbstractSelector
  64: {
  65:   // XXX is this reasonable? Does it matter?
  66:   private static final int DEFAULT_EPOLL_SIZE = 128;
  67:   private static final int sizeof_struct_epoll_event;
  68: 
  69:   private static final int OP_ACCEPT  = SelectionKey.OP_ACCEPT;
  70:   private static final int OP_CONNECT = SelectionKey.OP_CONNECT;
  71:   private static final int OP_READ    = SelectionKey.OP_READ;
  72:   private static final int OP_WRITE   = SelectionKey.OP_WRITE;
  73: 
  74:   /** our epoll file descriptor. */
  75:   private int epoll_fd;
  76: 
  77:   private final HashMap keys;
  78:   private Set selectedKeys;
  79:   private Thread waitingThread;
  80:   private ByteBuffer events;
  81: 
  82:   private static final int INITIAL_CAPACITY;
  83:   private static final int MAX_DOUBLING_CAPACITY;
  84:   private static final int CAPACITY_INCREMENT;
  85: 
  86:   static
  87:   {
  88:     if (Configuration.INIT_LOAD_LIBRARY)
  89:       System.loadLibrary("javanio");
  90: 
  91:     if (epoll_supported())
  92:       sizeof_struct_epoll_event = sizeof_struct();
  93:     else
  94:       sizeof_struct_epoll_event = -1;
  95: 
  96:     INITIAL_CAPACITY = 64 * sizeof_struct_epoll_event;
  97:     MAX_DOUBLING_CAPACITY = 1024 * sizeof_struct_epoll_event;
  98:     CAPACITY_INCREMENT = 128 * sizeof_struct_epoll_event;
  99:   }
 100: 
 101:   public EpollSelectorImpl(SelectorProvider provider)
 102:     throws IOException
 103:   {
 104:     super(provider);
 105:     epoll_fd = epoll_create(DEFAULT_EPOLL_SIZE);
 106:     keys = new HashMap();
 107:     selectedKeys = null;
 108:     events = ByteBuffer.allocateDirect(INITIAL_CAPACITY);
 109:   }
 110: 
 111:   /* (non-Javadoc)
 112:    * @see java.nio.channels.Selector#keys()
 113:    */
 114:   public Set keys()
 115:   {
 116:     return new HashSet(keys.values());
 117:   }
 118: 
 119:   /* (non-Javadoc)
 120:    * @see java.nio.channels.Selector#select()
 121:    */
 122:   public int select() throws IOException
 123:   {
 124:     return doSelect(-1);
 125:   }
 126: 
 127:   /* (non-Javadoc)
 128:    * @see java.nio.channels.Selector#select(long)
 129:    */
 130:   public int select(long timeout) throws IOException
 131:   {
 132:     if (timeout > Integer.MAX_VALUE)
 133:       throw new IllegalArgumentException("timeout is too large");
 134:     if (timeout < 0)
 135:       throw new IllegalArgumentException("invalid timeout");
 136:     return doSelect((int) timeout);
 137:   }
 138: 
 139:   private int doSelect(int timeout) throws IOException
 140:   {
 141:     synchronized (keys)
 142:     {
 143:       Set cancelledKeys = cancelledKeys();
 144:       synchronized (cancelledKeys)
 145:       {
 146:         for (Iterator it = cancelledKeys.iterator(); it.hasNext(); )
 147:           {
 148:             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
 149:             epoll_delete(epoll_fd, key.fd);
 150:             key.valid = false;
 151:             keys.remove(Integer.valueOf(key.fd));
 152:             it.remove();
 153:             deregister(key);
 154:           }
 155: 
 156:         // Clear out closed channels. The fds are removed from the epoll
 157:         // fd when closed, so there is no need to remove them manually.
 158:         for (Iterator it = keys.values().iterator(); it.hasNext(); )
 159:           {
 160:             EpollSelectionKeyImpl key = (EpollSelectionKeyImpl) it.next();
 161:             SelectableChannel ch = key.channel();
 162:             if (ch instanceof VMChannelOwner)
 163:               {
 164:                 if (!((VMChannelOwner) ch).getVMChannel().getState().isValid())
 165:                   it.remove();
 166:               }
 167:           }
 168: 
 169:         // Don't bother if we have nothing to select.
 170:         if (keys.isEmpty())
 171:           return 0;
 172: 
 173:         int ret;
 174:         try
 175:           {
 176:             begin();
 177:             waitingThread = Thread.currentThread();
 178:             ret = epoll_wait(epoll_fd, events, keys.size(), timeout);
 179:           }
 180:         finally
 181:           {
 182:             Thread.interrupted();
 183:             waitingThread = null;
 184:             end();
 185:           }
 186: 
 187:         HashSet s = new HashSet(ret);
 188:         for (int i = 0; i < ret; i++)
 189:           {
 190:             events.position(i * sizeof_struct_epoll_event);
 191:             ByteBuffer b = events.slice();
 192:             int fd = selected_fd(b);
 193:             EpollSelectionKeyImpl key
 194:               = (EpollSelectionKeyImpl) keys.get(Integer.valueOf(fd));
 195:             if (key == null)
 196:               throw new IOException("fd was selected, but no key found");
 197:             key.selectedOps = selected_ops(b) & key.interestOps;
 198:             s.add(key);
 199:           }
 200: 
 201:         reallocateBuffer();
 202: 
 203:         selectedKeys = s;
 204:         return ret;
 205:       }
 206:     }
 207:   }
 208: 
 209:   /* (non-Javadoc)
 210:    * @see java.nio.channels.Selector#selectedKeys()
 211:    */
 212:   public Set selectedKeys()
 213:   {
 214:     if (selectedKeys == null)
 215:       return Collections.EMPTY_SET;
 216:     return selectedKeys;
 217:   }
 218: 
 219:   /* (non-Javadoc)
 220:    * @see java.nio.channels.Selector#selectNow()
 221:    */
 222:   public int selectNow() throws IOException
 223:   {
 224:     return doSelect(0);
 225:   }
 226: 
 227:   /* (non-Javadoc)
 228:    * @see java.nio.channels.Selector#wakeup()
 229:    */
 230:   public Selector wakeup()
 231:   {
 232:     try
 233:       {
 234:         waitingThread.interrupt();
 235:       }
 236:     catch (NullPointerException npe)
 237:       {
 238:         // Ignored, thrown if we are not in a blocking op.
 239:       }
 240:     return this;
 241:   }
 242: 
 243:   /* (non-Javadoc)
 244:    * @see java.nio.channels.spi.AbstractSelector#implCloseSelector()
 245:    */
 246:   protected void implCloseSelector() throws IOException
 247:   {
 248:     VMChannel.close(epoll_fd);
 249:   }
 250: 
 251:   /* (non-Javadoc)
 252:    * @see java.nio.channels.spi.AbstractSelector#register(java.nio.channels.spi.AbstractSelectableChannel, int, java.lang.Object)
 253:    */
 254:   protected SelectionKey register(AbstractSelectableChannel ch, int ops, Object att)
 255:   {
 256:     if (!(ch instanceof VMChannelOwner))
 257:       throw new IllegalArgumentException("unsupported channel type");
 258: 
 259:     VMChannel channel = ((VMChannelOwner) ch).getVMChannel();
 260:     try
 261:       {
 262:         int native_fd = channel.getState().getNativeFD();
 263:         synchronized (keys)
 264:         {
 265:           if (keys.containsKey(Integer.valueOf(native_fd)))
 266:             throw new IllegalArgumentException("channel already registered");
 267:           EpollSelectionKeyImpl result =
 268:             new EpollSelectionKeyImpl(this, ch, native_fd);
 269:           if ((ops & ~(ch.validOps())) != 0)
 270:             throw new IllegalArgumentException("invalid ops for channel");
 271:           result.interestOps = ops;
 272:           result.selectedOps = 0;
 273:           result.valid = true;
 274:           result.attach(att);
 275:           result.key = System.identityHashCode(result);
 276:           epoll_add(epoll_fd, result.fd, ops);
 277:           keys.put(Integer.valueOf(native_fd), result);
 278:           reallocateBuffer();
 279:           return result;
 280:         }
 281:       }
 282:     catch (IOException ioe)
 283:       {
 284:         throw new IllegalArgumentException(ioe);
 285:       }
 286:   }
 287: 
 288:   private void reallocateBuffer()
 289:   {
 290:     // Ensure we have enough space for all potential events that may be
 291:     // returned.
 292:     if (events.capacity() < keys.size() * sizeof_struct_epoll_event)
 293:       {
 294:         int cap = events.capacity();
 295:         if (cap < MAX_DOUBLING_CAPACITY)
 296:           cap <<= 1;
 297:         else
 298:           cap += CAPACITY_INCREMENT;
 299:         events = ByteBuffer.allocateDirect(cap);
 300:       }
 301:     // Ensure that the events buffer is not too large, given the number of
 302:     // events registered.
 303:     else if (events.capacity() > keys.size() * sizeof_struct_epoll_event * 2 + 1
 304:              && events.capacity() > INITIAL_CAPACITY)
 305:       {
 306:         int cap = events.capacity() >>> 1;
 307:         events = ByteBuffer.allocateDirect(cap);
 308:       }
 309:   }
 310: 
 311:   void epoll_modify(EpollSelectionKeyImpl key, int ops) throws IOException
 312:   {
 313:     epoll_modify(epoll_fd, key.fd, ops);
 314:   }
 315: 
 316:   /**
 317:    * Tell if epoll is supported by this system, and support was compiled in.
 318:    *
 319:    * @return True if this system supports event notification with epoll.
 320:    */
 321:   public static native boolean epoll_supported();
 322: 
 323: 
 324:   /**
 325:    * Returns the size of `struct epoll_event'.
 326:    *
 327:    * @return The size of `struct epoll_event'.
 328:    */
 329:   private static native int sizeof_struct();
 330: 
 331: 
 332:   /**
 333:    * Open a new epoll file descriptor.
 334:    *
 335:    * @param size The size hint for the new epoll descriptor.
 336:    * @return The new file descriptor integer.
 337:    * @throws IOException If allocating a new epoll descriptor fails.
 338:    */
 339:   private static native int epoll_create(int size) throws IOException;
 340: 
 341:   /**
 342:    * Add a file descriptor to this selector.
 343:    *
 344:    * @param efd The epoll file descriptor.
 345:    * @param fd  The file descriptor to add (or modify).
 346:    * @param ops The interest opts.
 347:    */
 348:   private static native void epoll_add(int efd, int fd, int ops)
 349:     throws IOException;
 350: 
 351:   /**
 352:    * Modify the interest ops of the key selecting for the given FD.
 353:    *
 354:    * @param efd The epoll file descriptor.
 355:    * @param fd  The file descriptor to modify.
 356:    * @param ops The ops.
 357:    * @throws IOException
 358:    */
 359:   private static native void epoll_modify(int efd, int fd, int ops)
 360:     throws IOException;
 361: 
 362:   /**
 363:    * Remove a file descriptor from this selector.
 364:    *
 365:    * @param efd The epoll file descriptor.
 366:    * @param fd  The file descriptor.
 367:    * @throws IOException
 368:    */
 369:   private static native void epoll_delete(int efd, int fd) throws IOException;
 370: 
 371:   /**
 372:    * Select events.
 373:    *
 374:    * @param efd     The epoll file descriptor.
 375:    * @param state   The buffer to hold selected events.
 376:    * @param n       The number of events that may be put in `state'.
 377:    * @param timeout The timeout.
 378:    * @return The number of events selected.
 379:    * @throws IOException
 380:    */
 381:   private static native int epoll_wait(int efd, ByteBuffer state, int n, int timeout)
 382:     throws IOException;
 383: 
 384:   /**
 385:    * Fetch the fd value from a selected struct epoll_event.
 386:    *
 387:    * @param struct The direct buffer holding the struct.
 388:    * @return The fd value.
 389:    */
 390:   private static native int selected_fd(ByteBuffer struct);
 391: 
 392:   /**
 393:    * Fetch the enabled operations from a selected struct epoll_event.
 394:    *
 395:    * @param struct The direct buffer holding the struct.
 396:    * @return The selected operations.
 397:    */
 398:   private static native int selected_ops(ByteBuffer struct);
 399: }