| /* SelectorImpl.java -- | |
| Copyright (C) 2002, 2003, 2004, 2006 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.nio; | |
| import java.io.IOException; | |
| import java.nio.channels.ClosedSelectorException; | |
| import java.nio.channels.SelectableChannel; | |
| import java.nio.channels.SelectionKey; | |
| import java.nio.channels.Selector; | |
| import java.nio.channels.SocketChannel; | |
| import java.nio.channels.spi.AbstractSelectableChannel; | |
| import java.nio.channels.spi.AbstractSelector; | |
| import java.nio.channels.spi.SelectorProvider; | |
| import java.util.Collections; | |
| import java.util.HashSet; | |
| import java.util.Iterator; | |
| import java.util.Set; | |
| public class SelectorImpl extends AbstractSelector | |
| { | |
| private Set<SelectionKey> keys; | |
| private Set<SelectionKey> selected; | |
| /** | |
| * A dummy object whose monitor regulates access to both our | |
| * selectThread and unhandledWakeup fields. | |
| */ | |
| private Object selectThreadMutex = new Object (); | |
| /** | |
| * Any thread that's currently blocked in a select operation. | |
| */ | |
| private Thread selectThread; | |
| /** | |
| * Indicates whether we have an unhandled wakeup call. This can | |
| * be due to either wakeup() triggering a thread interruption while | |
| * a thread was blocked in a select operation (in which case we need | |
| * to reset this thread's interrupt status after interrupting the | |
| * select), or else that no thread was on a select operation at the | |
| * time that wakeup() was called, in which case the following select() | |
| * operation should return immediately with nothing selected. | |
| */ | |
| private boolean unhandledWakeup; | |
| public SelectorImpl (SelectorProvider provider) | |
| { | |
| super (provider); | |
| keys = new HashSet<SelectionKey> (); | |
| selected = new HashSet<SelectionKey> (); | |
| } | |
| protected void finalize() throws Throwable | |
| { | |
| close(); | |
| } | |
| protected final void implCloseSelector() | |
| throws IOException | |
| { | |
| // Cancel any pending select operation. | |
| wakeup(); | |
| synchronized (keys) | |
| { | |
| synchronized (selected) | |
| { | |
| synchronized (cancelledKeys ()) | |
| { | |
| // FIXME: Release resources here. | |
| } | |
| } | |
| } | |
| } | |
| public final Set<SelectionKey> keys() | |
| { | |
| if (!isOpen()) | |
| throw new ClosedSelectorException(); | |
| return Collections.unmodifiableSet (keys); | |
| } | |
| public final int selectNow() | |
| throws IOException | |
| { | |
| // FIXME: We're simulating an immediate select | |
| // via a select with a timeout of one millisecond. | |
| return select (1); | |
| } | |
| public final int select() | |
| throws IOException | |
| { | |
| return select (0); | |
| } | |
| private final int[] getFDsAsArray (int ops) | |
| { | |
| int[] result; | |
| int counter = 0; | |
| Iterator<SelectionKey> it = keys.iterator (); | |
| // Count the number of file descriptors needed | |
| while (it.hasNext ()) | |
| { | |
| SelectionKeyImpl key = (SelectionKeyImpl) it.next (); | |
| if ((key.interestOps () & ops) != 0) | |
| { | |
| counter++; | |
| } | |
| } | |
| result = new int[counter]; | |
| counter = 0; | |
| it = keys.iterator (); | |
| // Fill the array with the file descriptors | |
| while (it.hasNext ()) | |
| { | |
| SelectionKeyImpl key = (SelectionKeyImpl) it.next (); | |
| if ((key.interestOps () & ops) != 0) | |
| { | |
| result[counter] = key.getNativeFD(); | |
| counter++; | |
| } | |
| } | |
| return result; | |
| } | |
| public synchronized int select (long timeout) | |
| throws IOException | |
| { | |
| if (!isOpen()) | |
| throw new ClosedSelectorException(); | |
| synchronized (keys) | |
| { | |
| synchronized (selected) | |
| { | |
| deregisterCancelledKeys(); | |
| // Set only keys with the needed interest ops into the arrays. | |
| int[] read = getFDsAsArray (SelectionKey.OP_READ | |
| | SelectionKey.OP_ACCEPT); | |
| int[] write = getFDsAsArray (SelectionKey.OP_WRITE | |
| | SelectionKey.OP_CONNECT); | |
| // FIXME: We dont need to check this yet | |
| int[] except = new int [0]; | |
| // Test to see if we've got an unhandled wakeup call, | |
| // in which case we return immediately. Otherwise, | |
| // remember our current thread and jump into the select. | |
| // The monitor for dummy object selectThreadMutex regulates | |
| // access to these fields. | |
| // FIXME: Not sure from the spec at what point we should | |
| // return "immediately". Is it here or immediately upon | |
| // entry to this function? | |
| // NOTE: There's a possibility of another thread calling | |
| // wakeup() immediately after our thread releases | |
| // selectThreadMutex's monitor here, in which case we'll | |
| // do the select anyway. Since calls to wakeup() and select() | |
| // among different threads happen in non-deterministic order, | |
| // I don't think this is an issue. | |
| synchronized (selectThreadMutex) | |
| { | |
| if (unhandledWakeup) | |
| { | |
| unhandledWakeup = false; | |
| return 0; | |
| } | |
| else | |
| { | |
| selectThread = Thread.currentThread (); | |
| } | |
| } | |
| // Call the native select() on all file descriptors. | |
| int result = 0; | |
| try | |
| { | |
| begin(); | |
| result = VMSelector.select (read, write, except, timeout); | |
| } | |
| finally | |
| { | |
| end(); | |
| } | |
| // If our unhandled wakeup flag is set at this point, | |
| // reset our thread's interrupt flag because we were | |
| // awakened by wakeup() instead of an external thread | |
| // interruption. | |
| // | |
| // NOTE: If we were blocked in a select() and one thread | |
| // called Thread.interrupt() on the blocked thread followed | |
| // by another thread calling Selector.wakeup(), then race | |
| // conditions could make it so that the thread's interrupt | |
| // flag is reset even though the Thread.interrupt() call | |
| // "was there first". I don't think we need to care about | |
| // this scenario. | |
| synchronized (selectThreadMutex) | |
| { | |
| if (unhandledWakeup) | |
| { | |
| unhandledWakeup = false; | |
| Thread.interrupted (); | |
| } | |
| selectThread = null; | |
| } | |
| Iterator<SelectionKey> it = keys.iterator (); | |
| while (it.hasNext ()) | |
| { | |
| int ops = 0; | |
| SelectionKeyImpl key = (SelectionKeyImpl) it.next (); | |
| // If key is already selected retrieve old ready ops. | |
| if (selected.contains (key)) | |
| { | |
| ops = key.readyOps (); | |
| } | |
| // Set new ready read/accept ops | |
| for (int i = 0; i < read.length; i++) | |
| { | |
| if (key.getNativeFD() == read[i]) | |
| { | |
| if (key.channel () instanceof ServerSocketChannelImpl) | |
| { | |
| ops = ops | SelectionKey.OP_ACCEPT; | |
| } | |
| else | |
| { | |
| ops = ops | SelectionKey.OP_READ; | |
| } | |
| } | |
| } | |
| // Set new ready write ops | |
| for (int i = 0; i < write.length; i++) | |
| { | |
| if (key.getNativeFD() == write[i]) | |
| { | |
| if (key.channel() instanceof SocketChannel) | |
| { | |
| if (((SocketChannel) key.channel ()).isConnected ()) | |
| ops = ops | SelectionKey.OP_WRITE; | |
| else | |
| ops = ops | SelectionKey.OP_CONNECT; | |
| } | |
| else | |
| ops = ops | SelectionKey.OP_WRITE; | |
| } | |
| } | |
| // FIXME: We dont handle exceptional file descriptors yet. | |
| // If key is not yet selected add it. | |
| if (!selected.contains (key)) | |
| { | |
| selected.add (key); | |
| } | |
| // Set new ready ops | |
| key.readyOps (key.interestOps () & ops); | |
| } | |
| deregisterCancelledKeys(); | |
| return result; | |
| } | |
| } | |
| } | |
| public final Set<SelectionKey> selectedKeys() | |
| { | |
| if (!isOpen()) | |
| throw new ClosedSelectorException(); | |
| return selected; | |
| } | |
| public final Selector wakeup() | |
| { | |
| // IMPLEMENTATION NOTE: Whereas the specification says that | |
| // thread interruption should trigger a call to wakeup, we | |
| // do the reverse under the covers: wakeup triggers a thread | |
| // interrupt followed by a subsequent reset of the thread's | |
| // interrupt status within select(). | |
| // First, acquire the monitor of the object regulating | |
| // access to our selectThread and unhandledWakeup fields. | |
| synchronized (selectThreadMutex) | |
| { | |
| unhandledWakeup = true; | |
| // Interrupt any thread which is currently blocked in | |
| // a select operation. | |
| if (selectThread != null) | |
| selectThread.interrupt (); | |
| } | |
| return this; | |
| } | |
| private final void deregisterCancelledKeys() | |
| { | |
| Set<SelectionKey> ckeys = cancelledKeys (); | |
| synchronized (ckeys) | |
| { | |
| Iterator<SelectionKey> it = ckeys.iterator(); | |
| while (it.hasNext ()) | |
| { | |
| keys.remove ((SelectionKeyImpl) it.next ()); | |
| it.remove (); | |
| } | |
| } | |
| } | |
| protected SelectionKey register (SelectableChannel ch, int ops, Object att) | |
| { | |
| return register ((AbstractSelectableChannel) ch, ops, att); | |
| } | |
| protected final SelectionKey register (AbstractSelectableChannel ch, int ops, | |
| Object att) | |
| { | |
| SelectionKeyImpl result; | |
| if (ch instanceof SocketChannelImpl) | |
| result = new SocketChannelSelectionKey (ch, this); | |
| else if (ch instanceof DatagramChannelImpl) | |
| result = new DatagramChannelSelectionKey (ch, this); | |
| else if (ch instanceof ServerSocketChannelImpl) | |
| result = new ServerSocketChannelSelectionKey (ch, this); | |
| else if (ch instanceof gnu.java.nio.SocketChannelImpl) | |
| result = new gnu.java.nio.SocketChannelSelectionKeyImpl((gnu.java.nio.SocketChannelImpl)ch, this); | |
| else | |
| throw new InternalError ("No known channel type"); | |
| synchronized (keys) | |
| { | |
| keys.add (result); | |
| result.interestOps (ops); | |
| result.attach (att); | |
| } | |
| return result; | |
| } | |
| } |