[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