harmony-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From regi...@apache.org
Subject svn commit: r824614 - in /harmony/enhanced/classlib/trunk/modules: luni/src/main/java/org/apache/harmony/luni/platform/ nio/src/main/java/common/org/apache/harmony/nio/internal/ nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/
Date Tue, 13 Oct 2009 07:04:32 GMT
Author: regisxu
Date: Tue Oct 13 07:04:31 2009
New Revision: 824614

URL: http://svn.apache.org/viewvc?rev=824614&view=rev
Log:
Apply partial part of patch NIO_Concurrency_issues_2.patch for HARMONY-6312: Concurrency problems
in NIO

- Permit extra space in the arrays sent to INetworkSystem
- Move Javadoc from INetworkSystem implementation to interface
- Reuse arrays rather than reallocating them for both input and output of INetworkSystem
- Add tests for keySet viewing modifications and keys cancelled during select

Modified:
    harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
    harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
    harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectionKeyImpl.java
    harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
    harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java
    harmony/enhanced/classlib/trunk/modules/nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/SelectorTest.java

Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/INetworkSystem.java
Tue Oct 13 07:04:31 2009
@@ -156,8 +156,38 @@
     public InetAddress getSocketLocalAddress(FileDescriptor aFD,
             boolean preferIPv6Addresses);
 
-    public int[] select(FileDescriptor[] readFDs,
-            FileDescriptor[] writeFDs, long timeout)
+    /**
+     * Select the given file descriptors for read and write operations.
+     *
+     * <p>The first {@code numReadable} file descriptors of {@code readFDs} will
+     * be selected for read-ready operations. The first {@code numWritable} file
+     * descriptors in {@code writeFDs} will be selected for write-ready
+     * operations. A file descriptor can appear in either or both and must not
+     * be null. If the file descriptor is closed during the select the behavior
+     * depends upon the underlying OS.
+     *
+     * @param readFDs
+     *            all sockets interested in read and accept
+     * @param writeFDs
+     *            all sockets interested in write and connect
+     * @param numReadable
+     *            the size of the subset of readFDs to read or accept.
+     * @param numWritable
+     *            the size of the subset of writeFDs to write or connect
+     * @param timeout
+     *            timeout in milliseconds
+     * @param flags
+     *            for output. Length must be at least {@code numReadable
+     *            + numWritable}. Upon returning, each element describes the
+     *            state of the descriptor in the corresponding read or write
+     *            array. See {@code SelectorImpl.READABLE} and {@code
+     *            SelectorImpl.WRITEABLE}
+     * @return true
+     *            unless selection timed out or was interrupted
+     * @throws SocketException
+     */
+    public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
+            int numReadable, int numWritable, long timeout, int[] flags)
             throws SocketException;
 
     /*

Modified: harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/luni/src/main/java/org/apache/harmony/luni/platform/OSNetworkSystem.java
Tue Oct 13 07:04:31 2009
@@ -327,51 +327,29 @@
             DatagramPacket packet, long address, int offset, int length,
             int receiveTimeout, boolean peek) throws IOException;
 
-    /**
-     * Select the given file descriptors for read and write operations.
-     * 
-     * The file descriptors passed in as readFDs will be selected for read-ready
-     * operations, and those in the writeFDs will be selected for write-ready
-     * operations. A file descriptor can appear in either or both array, and
-     * must not be <code>null</code>. If the file descriptor is closed during
-     * the select the behavior depends upon the underlying OS.
-     * 
-     * Upon return the result is a single array of length
-     * <code>readFDs.length</code> + <code>writeFDs.length</code>
laid out as
-     * the result of the select operation on the corresponding file descriptors.
-     * 
-     * @param readFDs
-     *            all sockets interested in read and accept
-     * @param writeFDs
-     *            all sockets interested in write and connect
-     * @param timeout
-     *            timeout in milliseconds
-     * @return each element describes the corresponding state of the descriptor
-     *         in the read and write arrays.
-     * @throws SocketException
-     */
-    public int[] select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
-            long timeout) throws SocketException {
-        int countRead = readFDs.length;
-        int countWrite = writeFDs.length;
-        int result = 0;
-        if (0 == countRead + countWrite) {
-            return (new int[0]);
+
+    public boolean select(FileDescriptor[] readFDs, FileDescriptor[] writeFDs,
+            int numReadable, int numWritable, long timeout, int[] flags)
+            throws SocketException {
+        if (numReadable < 0 || numWritable < 0) {
+            throw new IllegalArgumentException();
         }
-        int[] flags = new int[countRead + countWrite];
 
-        assert validateFDs(readFDs, writeFDs) : "Invalid file descriptor arrays"; //$NON-NLS-1$
+        int total = numReadable + numWritable;
+        if (total == 0) {
+            return true;
+        }
 
-        // handle timeout in native
-        result = selectImpl(readFDs, writeFDs, countRead, countWrite, flags,
-                timeout);
+        assert validateFDs(readFDs, writeFDs, numReadable, numWritable) : "Invalid file descriptor
arrays"; //$NON-NLS-1$
 
-        if (0 <= result) {
-            return flags;
+        // handle timeout in native
+        int result = selectImpl(readFDs, writeFDs, numReadable, numWritable, flags, timeout);
+        if (result >= 0) {
+            return true;
         }
-        if (ERRORCODE_SOCKET_TIMEOUT == result ||
-            ERRORCODE_SOCKET_INTERRUPTED == result) {
-            return new int[0];
+        if (result == ERRORCODE_SOCKET_TIMEOUT ||
+                result == ERRORCODE_SOCKET_INTERRUPTED) {
+            return false;
         }
         throw new SocketException();
     }
@@ -497,6 +475,22 @@
         return true;
     }
 
+    private boolean validateFDs(FileDescriptor[] readFDs,
+            FileDescriptor[] writeFDs, int countRead, int countWrite) {
+        for (int i = 0; i < countRead; ++i) {
+            // Also checks fd not null
+            if (!readFDs[i].valid()) {
+                return false;
+            }
+        }
+        for (int i = 0; i < countWrite; ++i) {
+            if (!writeFDs[i].valid()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     /**
      * Write bytes from a byte array to a socket.
      * 

Modified: harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectionKeyImpl.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectionKeyImpl.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectionKeyImpl.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectionKeyImpl.java
Tue Oct 13 07:04:31 2009
@@ -20,16 +20,15 @@
 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.AbstractSelectionKey;
 
-/*
+/**
  * Default implementation of SelectionKey
  */
 final class SelectionKeyImpl extends AbstractSelectionKey {
 
-    static int stHash;
-
     private AbstractSelectableChannel channel;
 
     private int interestOps;
@@ -42,7 +41,6 @@
 
     public SelectionKeyImpl(AbstractSelectableChannel channel, int operations,
             Object attachment, SelectorImpl selector) {
-        super();
         this.channel = channel;
         interestOps = operations;
         this.selector = selector;
@@ -62,6 +60,12 @@
         }
     }
 
+    int interestOpsNoCheck() {
+        synchronized (selector.keysLock) {
+            return interestOps;
+        }
+    }
+
     @Override
     public SelectionKey interestOps(int operations) {
         checkValid();
@@ -106,4 +110,13 @@
             throw new CancelledKeyException();
         }
     }
+
+    /**
+     * Returns true if the channel for this key is connected. If the channel
+     * does not need connecting, this always return true.
+     */
+    boolean isConnected() {
+        return !(channel instanceof SocketChannel) 
+                || ((SocketChannel) channel).isConnected();
+    }
 }

Modified: harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/SelectorImpl.java
Tue Oct 13 07:04:31 2009
@@ -23,6 +23,10 @@
 import java.nio.channels.Pipe;
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
+import static java.nio.channels.SelectionKey.OP_ACCEPT;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
 import java.nio.channels.spi.AbstractSelectableChannel;
@@ -44,6 +48,10 @@
  */
 final class SelectorImpl extends AbstractSelector {
 
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
+    private static final int ACCEPT_OR_READ = OP_ACCEPT | OP_READ;
+
     private static final int MOCK_WRITEBUF_SIZE = 1;
 
     private static final int MOCK_READBUF_SIZE = 8;
@@ -58,31 +66,31 @@
 
     private static final int SELECT_NOW = 0;
 
-    /*
-     * keysLock is used to brief synchronization when get selectionKeys snapshot
-     * before selection.
+    /**
+     * Used to synchronize when a key's interest ops change.
      */
     private static class KeysLock {}
     final Object keysLock = new KeysLock();
 
     private SelectionKey[] keys = new SelectionKey[1];
 
-    private final Set<SelectionKey> keysSet = new HashSet<SelectionKey>();
+    private final Set<SelectionKey> mutableKeys = new HashSet<SelectionKey>();
 
+    /**
+     * The unmodifiable set of keys as exposed to the user. This object is used
+     * for synchronization.
+     */
     private Set<SelectionKey> unmodifiableKeys = Collections
-            .unmodifiableSet(keysSet);
+            .<SelectionKey>unmodifiableSet(mutableKeys);
 
-    private final Set<SelectionKey> selectedKeys = new HashSet<SelectionKey>();
+    private final Set<SelectionKey> mutableSelectedKeys = new HashSet<SelectionKey>();
 
-    private Set<SelectionKey> unaddableSelectedKeys = new UnaddableSet<SelectionKey>(
-            selectedKeys);
-
-    // sink and source are used by wakeup()
-    private Pipe.SinkChannel sink;
-
-    private Pipe.SourceChannel source;
-
-    private FileDescriptor sourcefd;
+    /**
+     * The unmodifiable set of selectable keys as seen by the user. This object
+     * is used for synchronization.
+     */
+    private final Set<SelectionKey> selectedKeys
+            = new UnaddableSet<SelectionKey>(mutableSelectedKeys);
 
     private FileDescriptor[] readableFDs;
 
@@ -102,6 +110,22 @@
 
     private int[] writableFDsToKeys;
 
+    /**
+     * Selection flags that define the ready ops on the ready keys. When not
+     * actively selecting, all elements are 0. Corresponds to the ready keys
+     * set.
+     */
+    private int[] flags = EMPTY_INT_ARRAY;
+
+    /**
+     * A mock channel is used to signal wakeups. Whenever the selector should
+     * stop blocking on a select(), a byte is written to the sink and will be
+     * picked up in source by the selecting thread.
+     */
+    private Pipe.SinkChannel sink;
+    private Pipe.SourceChannel source;
+    private FileDescriptor sourcefd;
+
     public SelectorImpl(SelectorProvider selectorProvider) {
         super(selectorProvider);
         try {
@@ -143,10 +167,8 @@
             synchronized (unmodifiableKeys) {
                 synchronized (selectedKeys) {
                     doCancel();
-                    for (SelectionKey sk : keys) {
-                        if (sk != null) {
-                            deregister((AbstractSelectionKey) sk);
-                        }
+                    for (SelectionKey sk : mutableKeys) {
+                        deregister((AbstractSelectionKey) sk);
                     }
                 }
             }
@@ -391,15 +413,13 @@
         }
         synchronized (this) {
             synchronized (unmodifiableKeys) {
-
                 // create the key
-                SelectionKey sk = new SelectionKeyImpl(channel, operations,
-                        attachment, this);
-
-                int index = addKey(sk);
-                ((SelectionKeyImpl) sk).setIndex(index);
-
-                return sk;
+                SelectionKeyImpl selectionKey = new SelectionKeyImpl(
+                        channel, operations, attachment, this);
+                int index = addKey(selectionKey);
+                selectionKey.setIndex(index);
+                mutableKeys.add(selectionKey);
+                return selectionKey;
             }
         }
     }
@@ -410,18 +430,6 @@
     @Override
     public synchronized Set<SelectionKey> keys() {
         closeCheck();
-
-        keysSet.clear();
-
-        if (keys.length != lastKeyIndex + 1) {
-            SelectionKey[] chompedKeys = new SelectionKey[lastKeyIndex + 1];
-            System.arraycopy(keys, 0, chompedKeys, 0, lastKeyIndex + 1);
-            keysSet.addAll(Arrays.asList(chompedKeys));
-        } else {
-            keysSet.addAll(Arrays.asList(keys));
-        }
-
-        keysSet.remove(source.keyFor(this));
         return unmodifiableKeys;
     }
 
@@ -467,70 +475,50 @@
             synchronized (unmodifiableKeys) {
                 synchronized (selectedKeys) {
                     doCancel();
-                    int[] readyChannels = null;
                     boolean isBlock = (SELECT_NOW != timeout);
                     prepareChannels();
+                    boolean success;
                     try {
                         if (isBlock) {
                             begin();
                         }
-                        readyChannels = Platform.getNetworkSystem().select(
-                                readableFDs, writableFDs, timeout);
+                        success = Platform.getNetworkSystem().select(
+                                readableFDs, writableFDs, readableKeysCount, writableKeysCount,
timeout, flags);
                     } finally {
                         if (isBlock) {
                             end();
                         }
                     }
-                    return processSelectResult(readyChannels);
+
+                    int selected = success ? processSelectResult() : 0;
+
+                    Arrays.fill(flags, 0);
+
+                    doCancel();
+
+                    return selected;
                 }
             }
         }
     }
 
-    private boolean isConnected(SelectionKeyImpl key) {
-        SelectableChannel channel = key.channel();
-        if (channel instanceof SocketChannel) {
-            return ((SocketChannel) channel).isConnected();
-        }
-        return true;
-    }
-
     /*
      * Prepares and adds channels to list for selection
      */
     private void prepareChannels() {
-
-        // chomp the arrays if needed
-
-        if (readableFDs.length != readableKeysCount) {
-            FileDescriptor[] chompedReadableFDs = new FileDescriptor[readableKeysCount];
-            System.arraycopy(readableFDs, 0, chompedReadableFDs, 0,
-                    readableKeysCount);
-            readableFDs = chompedReadableFDs;
-        }
-
-        if (writableFDs.length != writableKeysCount) {
-            FileDescriptor[] chompedWriteableFDs = new FileDescriptor[writableKeysCount];
-            System.arraycopy(writableFDs, 0, chompedWriteableFDs, 0,
-                    writableKeysCount);
-            writableFDs = chompedWriteableFDs;
+        if (flags.length < readableKeysCount + writableKeysCount) {
+            flags = new int[readableKeysCount + writableKeysCount];
         }
 
     }
 
-    /*
-     * Analyses selected channels and adds keys of ready channels to
-     * selectedKeys list.
-     * 
-     * readyChannels are encoded as concatenated array of flags for readable
-     * channels followed by writable channels.
+    /**
+     * Updates the key ready ops and selected key set with data from the flags
+     * array.
      */
-    private int processSelectResult(int[] readyChannels) throws IOException {
-        if (0 == readyChannels.length) {
-            return 0;
-        }
+    private int processSelectResult() throws IOException {
         // if the mock channel is selected, read the content.
-        if (READABLE == readyChannels[0]) {
+        if (READABLE == flags[0]) {
             ByteBuffer readbuf = ByteBuffer.allocate(MOCK_READBUF_SIZE);
             while (source.read(readbuf) > 0) {
                 readbuf.flip();
@@ -538,47 +526,44 @@
         }
         int selected = 0;
 
-        for (int i = 1; i < readyChannels.length; i++) {
-
-            if (readyChannels[i] != NA) {
-                SelectionKeyImpl key = (SelectionKeyImpl) (i >= readableKeysCount ? keys[writableFDsToKeys[i
-                        - readableKeysCount]]
-                        : keys[readableFDsToKeys[i]]);
-
-                if (null == key) {
-                    continue;
-                }
-
-                int ops = key.interestOps();
-                int selectedOp = 0;
-
-                switch (readyChannels[i]) {
-
-                    case READABLE:
-                        selectedOp = (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)
-                                & ops;
-                        break;
-                    case WRITEABLE:
-                        if (isConnected(key)) {
-                            selectedOp = SelectionKey.OP_WRITE & ops;
-                        } else {
-                            selectedOp = SelectionKey.OP_CONNECT & ops;
-                        }
-                        break;
-                }
-
-                if (0 != selectedOp) {
-                    boolean isOldSelectedKey = selectedKeys.contains(key);
-                    if (isOldSelectedKey && key.readyOps() != selectedOp) {
-                        key.setReadyOps(key.readyOps() | selectedOp);
-                        selected++;
-                    } else if (!isOldSelectedKey) {
-                        key.setReadyOps(selectedOp);
-                        selectedKeys.add(key);
-                        selected++;
+        for (int i = 1; i < flags.length; i++) {
+            if (flags[i] == NA) {
+                continue;
+            }
+            SelectionKeyImpl key = (SelectionKeyImpl) (i >= readableKeysCount ? keys[writableFDsToKeys[i
+                    - readableKeysCount]]
+                    : keys[readableFDsToKeys[i]]);
+
+            if (null == key) {
+                continue;
+            }
+
+            int ops = key.interestOpsNoCheck();
+            int selectedOp = 0;
+
+            switch (flags[i]) {
+                case READABLE:
+                    selectedOp = ACCEPT_OR_READ & ops;
+                    break;
+                case WRITEABLE:
+                    if (key.isConnected()) {
+                        selectedOp = OP_WRITE & ops;
+                    } else {
+                        selectedOp = OP_CONNECT & ops;
                     }
-                }
+                    break;
+            }
 
+            if (0 != selectedOp) {
+                boolean wasSelected = mutableSelectedKeys.contains(key);
+                if (wasSelected && key.readyOps() != selectedOp) {
+                    key.setReadyOps(key.readyOps() | selectedOp);
+                    selected++;
+                } else if (!wasSelected) {
+                    key.setReadyOps(selectedOp);
+                    mutableSelectedKeys.add(key);
+                    selected++;
+                }
             }
         }
 
@@ -591,7 +576,7 @@
     @Override
     public synchronized Set<SelectionKey> selectedKeys() {
         closeCheck();
-        return unaddableSelectedKeys;
+        return selectedKeys;
     }
 
     /*
@@ -603,11 +588,12 @@
             if (cancelledKeys.size() > 0) {
                 for (SelectionKey currentkey : cancelledKeys) {
                     delKey(currentkey);
+                    mutableKeys.remove(currentkey);
                     deregister((AbstractSelectionKey) currentkey);
-                    selectedKeys.remove(currentkey);
+                    mutableSelectedKeys.remove(currentkey);
                 }
+                cancelledKeys.clear();
             }
-            cancelledKeys.clear();
             limitCapacity();
         }
     }
@@ -627,7 +613,7 @@
 
     private static class UnaddableSet<E> implements Set<E> {
 
-        private Set<E> set;
+        private final Set<E> set;
 
         UnaddableSet(Set<E> set) {
             this.set = set;

Modified: harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/nio/src/main/java/common/org/apache/harmony/nio/internal/ServerSocketChannelImpl.java
Tue Oct 13 07:04:31 2009
@@ -124,10 +124,11 @@
                     if (!isBlocking) {
                         // for non blocking mode, use select to see whether
                         // there are any pending connections.
-                        int[] tryResult = Platform.getNetworkSystem().select(
+                        int[] tryResult = new int[1];
+                        boolean success = Platform.getNetworkSystem().select(
                                 new FileDescriptor[] { this.fd },
-                                new FileDescriptor[0], 0);
-                        if (0 == tryResult.length || 0 == tryResult[0]) {
+                                new FileDescriptor[0], 1, 0, 0, tryResult);
+                        if (!success || 0 == tryResult[0]) {
                             // no pending connections, returns immediately.
                             return null;
                         }

Modified: harmony/enhanced/classlib/trunk/modules/nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/SelectorTest.java
URL: http://svn.apache.org/viewvc/harmony/enhanced/classlib/trunk/modules/nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/SelectorTest.java?rev=824614&r1=824613&r2=824614&view=diff
==============================================================================
--- harmony/enhanced/classlib/trunk/modules/nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/SelectorTest.java
(original)
+++ harmony/enhanced/classlib/trunk/modules/nio/src/test/java/common/org/apache/harmony/nio/tests/java/nio/channels/SelectorTest.java
Tue Oct 13 07:04:31 2009
@@ -19,6 +19,8 @@
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.ClosedSelectorException;
@@ -26,8 +28,12 @@
 import java.nio.channels.Selector;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
+import java.nio.channels.Pipe;
 import java.nio.channels.spi.SelectorProvider;
 import java.util.Set;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import junit.framework.TestCase;
 import tests.support.Support_PortManager;
@@ -337,6 +343,68 @@
         selectOnce(SelectType.TIMEOUT, 0);
     }
 
+    public void test_keySetViewsModifications() throws IOException {
+        Set<SelectionKey> keys = selector.keys();
+
+        SelectionKey key1 = ssc.register(selector, SelectionKey.OP_ACCEPT);
+
+        assertTrue(keys.contains(key1));
+
+        SocketChannel sc = SocketChannel.open();
+        sc.configureBlocking(false);
+        SelectionKey key2 = sc.register(selector, SelectionKey.OP_READ);
+
+        assertTrue(keys.contains(key1));
+        assertTrue(keys.contains(key2));
+
+        key1.cancel();
+        assertTrue(keys.contains(key1));
+
+        selector.selectNow();
+        assertFalse(keys.contains(key1));
+        assertTrue(keys.contains(key2));
+     }
+
+    /**
+     * This test cancels a key while selecting to verify that the cancelled
+     * key set is processed both before and after the call to the underlying
+     * operating system.
+     */
+    public void test_cancelledKeys() throws Exception {
+        final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
+        final AtomicBoolean complete = new AtomicBoolean();
+
+        final Pipe pipe = Pipe.open();
+        pipe.source().configureBlocking(false);
+        final SelectionKey key = pipe.source().register(selector, SelectionKey.OP_READ);
+
+        Thread thread = new Thread() {
+            public void run() {
+                try {
+                    // make sure to call key.cancel() while the main thread is selecting
+                    Thread.sleep(500);
+                    key.cancel();
+                    assertFalse(key.isValid());
+                    pipe.sink().write(ByteBuffer.allocate(4)); // unblock select()
+                } catch (Throwable e) {
+                    failure.set(e);
+                } finally {
+                    complete.set(true);
+                }
+            }
+        };
+        assertTrue(key.isValid());
+
+        thread.start();
+        do {
+            assertEquals(0, selector.select(5000)); // blocks
+        } while (!complete.get()); // avoid spurious interrupts
+        assertFalse(key.isValid());
+
+        thread.join();
+        assertNull(failure.get());
+    }
+
     private void assert_select_SelectorClosed(SelectType type, int timeout)
             throws IOException {
         // selector is closed



Mime
View raw message