[PATCH] 6350055: Atomic SelectionKey operations

David Lloyd david.lloyd at redhat.com
Thu May 31 14:55:02 UTC 2018


And here [1] it is.

[1] https://github.com/dmlloyd/openjdk/commit/atomic-nio-ops-v9

On Thu, May 31, 2018 at 9:52 AM, David Lloyd <david.lloyd at redhat.com> wrote:
> The testNioImplementation method covers IllegalArgumentException, but
> I can add tests for CancelledKeyException easily...
>
> On Thu, May 31, 2018 at 7:25 AM, Alan Bateman <Alan.Bateman at oracle.com> wrote:
>>
>>
>> On 30/05/2018 19:14, David Lloyd wrote:
>>>
>>> On Wed, May 23, 2018 at 9:00 AM, David Lloyd <david.lloyd at redhat.com>
>>> wrote:
>>>>
>>>> I can probably add tests to show that the ops work.  I'm not so sure
>>>> that I can make a test that proves the atomicity of the operation
>>>> though.
>>>
>>> Updated with tests [1] & attached.
>>>
>> I quickly skimmed it and the test look reasonable. We should extend it to
>> cover the CancelledKeyException and IllegaArgumentException cases too, I
>> didn't spot anything testing these exceptions.
>>
>> -Alan
>
>
>
> --
> - DML



-- 
- DML
-------------- next part --------------
commit 9469d5482558fc79e6ddbc2e84856431de213c44
Author: David M. Lloyd <david.lloyd at redhat.com>
Date:   Wed Mar 28 13:13:44 2018 -0500

    [JDK-6350055] Add atomic interest op methods to SelectionKey

diff --git a/src/java.base/share/classes/java/nio/channels/SelectionKey.java b/src/java.base/share/classes/java/nio/channels/SelectionKey.java
index ad08fe43822..cfc4faeae8e 100644
--- a/src/java.base/share/classes/java/nio/channels/SelectionKey.java
+++ b/src/java.base/share/classes/java/nio/channels/SelectionKey.java
@@ -189,6 +189,86 @@ public abstract class SelectionKey {
      */
     public abstract SelectionKey interestOps(int ops);
 
+    /**
+     * Atomically sets this key's interest set to bitwise union ("or") of the existing
+     * interest set and the given value. This method is guaranteed to be
+     * atomic with respect to other concurrent calls to this method or to
+     * {@link #interestOpsAnd(int)}.
+     *
+     * <p> This method may be invoked at any time.  If this method is invoked
+     * while a selection operation is in progress then it has no effect upon
+     * that operation; the change to the key's interest set will be seen by the
+     * next selection operation.
+     *
+     * @param  ops  The interest set to apply
+     *
+     * @return  The previous interest set
+     *
+     * @throws  IllegalArgumentException
+     *          If a bit in the resultant set does not correspond to an operation that
+     *          is supported by this key's channel, that is, if
+     *          {@code ((interestOps() | ops) & ~channel().validOps()) != 0}
+     *
+     * @throws  CancelledKeyException
+     *          If this key has been cancelled
+     *
+     * @implSpec The default implementation synchronizes on this {@code SelectionKey}
+     *           instance, calling {@link #interestOps()} and {@link #interestOps(int)}
+     *           in turn; subclasses should provide a better implementation if
+     *           possible (for example, using a
+     *           {@link java.lang.invoke.VarHandle VarHandle} may be appropriate).
+     */
+    public int interestOpsOr(int ops) {
+        synchronized (this) {
+            int oldVal = interestOps();
+            interestOps(oldVal | ops);
+            return oldVal;
+        }
+    }
+
+    /**
+     * Atomically sets this key's interest set to bitwise intersection ("and") of the
+     * existing interest set and the given value. This method is guaranteed to be
+     * atomic with respect to other concurrent calls to this method or to
+     * {@link #interestOpsOr(int)}.
+     *
+     * <p> This method may be invoked at any time.  If this method is invoked
+     * while a selection operation is in progress then it has no effect upon
+     * that operation; the change to the key's interest set will be seen by the
+     * next selection operation.
+     *
+     * @param  ops  The interest set to apply
+     *
+     * @return  The previous interest set
+     *
+     * @throws  IllegalArgumentException
+     *          If a bit in the resultant set does not correspond to an operation that
+     *          is supported by this key's channel, that is, if
+     *          {@code (interestOps() & ops & ~channel().validOps()) != 0}
+     *
+     * @throws  CancelledKeyException
+     *          If this key has been cancelled
+     *
+     * @apiNote The {@code ops} argument may contain bits which are not normally
+     *          allowed by this key's channel, allowing bits to be cleared using
+     *          bitwise complement values.  For example,
+     *          {@code interestOpsAnd(~SelectionKey.OP_READ)} will remove the
+     *          {@code OP_READ} bit from the set without affecting other bits.
+     *
+     * @implSpec The default implementation synchronizes on this {@code SelectionKey}
+     *           instance, calling {@link #interestOps()} and {@link #interestOps(int)}
+     *           in turn; subclasses should provide a better implementation if
+     *           possible (for example, using a
+     *           {@link java.lang.invoke.VarHandle VarHandle} may be appropriate).
+     */
+    public int interestOpsAnd(int ops) {
+        synchronized (this) {
+            int oldVal = interestOps();
+            interestOps(oldVal & ops);
+            return oldVal;
+        }
+    }
+
     /**
      * Retrieves this key's ready-operation set.
      *
diff --git a/src/java.base/share/classes/sun/nio/ch/SelectionKeyImpl.java b/src/java.base/share/classes/sun/nio/ch/SelectionKeyImpl.java
index 2f917c8d2e3..26b2afe2f08 100644
--- a/src/java.base/share/classes/sun/nio/ch/SelectionKeyImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/SelectionKeyImpl.java
@@ -25,6 +25,9 @@
 
 package sun.nio.ch;
 
+import java.lang.invoke.ConstantBootstraps;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
 import java.nio.channels.CancelledKeyException;
 import java.nio.channels.SelectableChannel;
 import java.nio.channels.SelectionKey;
@@ -42,6 +45,10 @@ public final class SelectionKeyImpl
     private final SelChImpl channel;
     private final SelectorImpl selector;
 
+    private static final VarHandle interestOpsHandle = ConstantBootstraps.fieldVarHandle(
+        MethodHandles.lookup(), "interestOps", VarHandle.class, SelectionKeyImpl.class, int.class
+    );
+
     private volatile int interestOps;
     private volatile int readyOps;
 
@@ -84,7 +91,35 @@ public final class SelectionKeyImpl
     @Override
     public SelectionKey interestOps(int ops) {
         ensureValid();
-        return nioInterestOps(ops);
+        if ((ops & ~channel().validOps()) != 0)
+            throw new IllegalArgumentException();
+        int oldOps = (int) interestOpsHandle.getAndSet(this, ops);
+        if (ops != oldOps) {
+            selector.setEventOps(this);
+        }
+        return this;
+    }
+
+    @Override
+    public int interestOpsOr(final int ops) {
+        ensureValid();
+        if ((ops & ~channel().validOps()) != 0)
+            throw new IllegalArgumentException();
+        int oldVal = (int) interestOpsHandle.getAndBitwiseOr(this, ops);
+        if (oldVal != (oldVal | ops)) {
+            selector.setEventOps(this);
+        }
+        return oldVal;
+    }
+
+    @Override
+    public int interestOpsAnd(final int ops) {
+        ensureValid();
+        int oldVal = (int) interestOpsHandle.getAndBitwiseAnd(this, ops);
+        if (oldVal != (oldVal & ops)) {
+            selector.setEventOps(this);
+        }
+        return oldVal;
     }
 
     @Override
diff --git a/test/jdk/java/nio/channels/Selector/AtomicSelectionKeyOps.java b/test/jdk/java/nio/channels/Selector/AtomicSelectionKeyOps.java
new file mode 100644
index 00000000000..4c11e92aa22
--- /dev/null
+++ b/test/jdk/java/nio/channels/Selector/AtomicSelectionKeyOps.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run testng ValidateSelectionKeyOps
+ * @summary Test that the operations on SelectionKey function as specified.
+ */
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.nio.channels.CancelledKeyException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import org.testng.annotations.Test;
+
+import static java.nio.channels.SelectionKey.OP_READ;
+import static java.nio.channels.SelectionKey.OP_WRITE;
+import static java.nio.channels.SelectionKey.OP_CONNECT;
+import static java.nio.channels.SelectionKey.OP_ACCEPT;
+import static org.testng.Assert.*;
+
+ at Test
+public class AtomicSelectionKeyOps {
+
+    public void testBaseImplementation() throws Exception {
+        SelectionKey testKeyImpl = new SelectionKey() {
+            private int ops;
+
+            public SelectableChannel channel() {
+                return null;
+            }
+
+            public Selector selector() {
+                return null;
+            }
+
+            public boolean isValid() {
+                return true;
+            }
+
+            public void cancel() {
+            }
+
+            public int interestOps() {
+                return ops;
+            }
+
+            public SelectionKey interestOps(final int ops) {
+                this.ops = ops;
+                return this;
+            }
+
+            public int readyOps() {
+                return 0;
+            }
+        };
+
+        assertEquals(testKeyImpl.interestOps(), 0);
+        testKeyImpl.interestOps(OP_READ);
+        assertEquals(testKeyImpl.interestOps(), OP_READ);
+        testKeyImpl.interestOpsOr(OP_ACCEPT | OP_WRITE);
+        assertEquals(testKeyImpl.interestOps(), OP_READ | OP_WRITE | OP_ACCEPT);
+        testKeyImpl.interestOpsAnd(OP_WRITE | OP_CONNECT);
+        assertEquals(testKeyImpl.interestOps(), OP_WRITE);
+        testKeyImpl.interestOpsOr(OP_CONNECT);
+        assertEquals(testKeyImpl.interestOps(), OP_WRITE | OP_CONNECT);
+        testKeyImpl.interestOpsAnd(OP_READ);
+        assertEquals(testKeyImpl.interestOps(), 0);
+    }
+
+    public void testNioImplementation() throws Exception {
+        final Selector selector = Selector.open();
+        final ConnectionPair pair = new ConnectionPair();
+        final SocketChannel channel = pair.channel1();
+        channel.configureBlocking(false);
+        final SelectionKey selectionKey = channel.register(selector, 0);
+        assertEquals(selectionKey.interestOps(), 0);
+        selectionKey.interestOps(OP_READ);
+        assertEquals(selectionKey.interestOps(), OP_READ);
+        try {
+            selectionKey.interestOpsOr(OP_ACCEPT | OP_WRITE);
+            fail("Expected exception");
+        } catch (IllegalArgumentException okay) {}
+        selectionKey.interestOpsAnd(OP_ACCEPT | OP_READ); // no failure
+        assertEquals(selectionKey.interestOps(), OP_READ);
+        selectionKey.interestOpsOr(OP_WRITE);
+        assertEquals(selectionKey.interestOps(), OP_READ | OP_WRITE);
+        selectionKey.interestOpsAnd(OP_WRITE | OP_CONNECT);
+        assertEquals(selectionKey.interestOps(), OP_WRITE);
+        selectionKey.interestOpsOr(OP_CONNECT);
+        assertEquals(selectionKey.interestOps(), OP_WRITE | OP_CONNECT);
+        selectionKey.interestOpsAnd(OP_READ);
+        assertEquals(selectionKey.interestOps(), 0);
+        selectionKey.cancel();
+        try {
+            selectionKey.interestOps(OP_READ); // baseline
+            fail("Expected exception");
+        } catch (CancelledKeyException okay) {}
+        try {
+            selectionKey.interestOpsAnd(OP_READ);
+            fail("Expected exception");
+        } catch (CancelledKeyException okay) {}
+        try {
+            selectionKey.interestOpsOr(OP_READ);
+            fail("Expected exception");
+        } catch (CancelledKeyException okay) {}
+        pair.close();
+    }
+
+    static class ConnectionPair implements Closeable {
+
+        private final SocketChannel sc1;
+        private final SocketChannel sc2;
+
+        ConnectionPair() throws IOException {
+            InetAddress lb = InetAddress.getLoopbackAddress();
+            try (ServerSocketChannel ssc = ServerSocketChannel.open()) {
+                ssc.bind(new InetSocketAddress(lb, 0));
+                this.sc1 = SocketChannel.open(ssc.getLocalAddress());
+                this.sc2 = ssc.accept();
+            }
+        }
+
+        SocketChannel channel1() {
+            return sc1;
+        }
+
+        SocketChannel channel2() {
+            return sc2;
+        }
+
+        public void close() throws IOException {
+            sc1.close();
+            sc2.close();
+        }
+    }
+}
+


More information about the nio-dev mailing list