From 19be0e7acd1629dc6780a324cae86ecb19c55e2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Wed, 9 Apr 2025 15:10:07 +0200 Subject: [PATCH 1/5] 8354024: [JMH] Create ephemeral UnixDomainSocketAddress provider with thread-safe close semantics --- .../java/net/ServerUdsChannelHolder.java | 75 +++++++++++++++++++ .../net/SocketChannelConnectionSetup.java | 66 ++++++---------- .../java/net/UnixSocketChannelReadWrite.java | 23 ++---- 3 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java diff --git a/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java b/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java new file mode 100644 index 0000000000000..c1db1ecd79a65 --- /dev/null +++ b/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2020, 2025, 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. + * + * 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. + */ +package org.openjdk.bench.java.net; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; +import java.nio.channels.ServerSocketChannel; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * A thread-safe utility class to create and destroy {@link ServerSocketChannel}s using unix domain sockets. + */ +final class ServerUdsChannelHolder implements AutoCloseable { + + private final Path socketFilePath; + + final ServerSocketChannel channel; + + private ServerUdsChannelHolder(String tempDirPrefix) { + try { + // Socket file will be created by `bind()`, hence, we must point to a non-existent file. + this.socketFilePath = Files.createTempDirectory(tempDirPrefix).resolve("sock"); + UnixDomainSocketAddress socketAddress = UnixDomainSocketAddress.of(socketFilePath); + this.channel = ServerSocketChannel + .open(StandardProtocolFamily.UNIX) + .bind(socketAddress); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + static ServerUdsChannelHolder forClass(Class clazz) { + return new ServerUdsChannelHolder(clazz.getSimpleName() + '-'); + } + + @Override + public void close() { + try { + channel.close(); + Files.delete(socketFilePath); + Files.delete(socketFilePath.getParent()); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + @Override + public String toString() { + return "" + socketFilePath; + } + +} diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java index 505e124e43ee6..f7293cd99b867 100644 --- a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java @@ -23,13 +23,9 @@ package org.openjdk.bench.java.net; import java.io.IOException; -import java.net.StandardProtocolFamily; -import java.net.UnixDomainSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.nio.file.*; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; @@ -48,64 +44,44 @@ @Fork(value = 3) public class SocketChannelConnectionSetup { - private ServerSocketChannel ssc; - private SocketChannel s1, s2; + private ServerSocketChannelHolder sscHolder; - private static volatile String tempDir; - private static final AtomicInteger count = new AtomicInteger(0); - private volatile Path socket; + private SocketChannel s1, s2; @Param({"inet", "unix"}) private volatile String family; - static { - try { - Path p = Files.createTempDirectory("readWriteTest"); - tempDir = p.toString(); - } catch (IOException e) { - tempDir = null; - } - } - - private ServerSocketChannel getServerSocketChannel() throws IOException { - if (family.equals("inet")) - return getInetServerSocketChannel(); - else if (family.equals("unix")) - return getUnixServerSocketChannel(); - throw new InternalError(); - } - - - private ServerSocketChannel getInetServerSocketChannel() throws IOException { - return ServerSocketChannel.open().bind(null); - } - - private ServerSocketChannel getUnixServerSocketChannel() throws IOException { - int next = count.incrementAndGet(); - socket = Paths.get(tempDir, Integer.toString(next)); - UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); - return ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(addr); + private record ServerSocketChannelHolder(ServerSocketChannel channel, AutoCloseable terminator) {} + + private ServerSocketChannelHolder createServerSocketChannelHolder() throws IOException { + return switch (family) { + case "inet" -> { + ServerSocketChannel channel = ServerSocketChannel.open().bind(null); + yield new ServerSocketChannelHolder(channel, channel); + } + case "unix" -> { + ServerUdsChannelHolder holder = ServerUdsChannelHolder.forClass(SocketChannelConnectionSetup.class); + yield new ServerSocketChannelHolder(holder.channel, holder); + } + default -> throw new InternalError("unknown family: " + family); + }; } @Setup(Level.Trial) public void beforeRun() throws IOException { - ssc = getServerSocketChannel(); + sscHolder = createServerSocketChannelHolder(); } @TearDown(Level.Trial) - public void afterRun() throws IOException { - ssc.close(); - if (family.equals("unix")) { - Files.deleteIfExists(socket); - Files.deleteIfExists(Path.of(tempDir)); - } + public void afterRun() throws Exception { + sscHolder.terminator.close(); } @Benchmark @Measurement(iterations = 5, batchSize=200) public void test() throws IOException { - s1 = SocketChannel.open(ssc.getLocalAddress()); - s2 = ssc.accept(); + s1 = SocketChannel.open(sscHolder.channel.getLocalAddress()); + s2 = sscHolder.channel.accept(); s1.close(); s2.close(); } diff --git a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java index 64b44446bcdba..7196ee0e8ad7c 100644 --- a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java +++ b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java @@ -23,11 +23,8 @@ package org.openjdk.bench.java.net; import java.io.IOException; -import java.net.StandardProtocolFamily; -import java.net.UnixDomainSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; -import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.*; import java.util.concurrent.TimeUnit; @@ -46,26 +43,18 @@ @Fork(value = 3) public class UnixSocketChannelReadWrite { - private ServerSocketChannel ssc; + private ServerUdsChannelHolder sscHolder; private SocketChannel s1, s2; private ReadThread rt; private ByteBuffer bb = ByteBuffer.allocate(1); private volatile Path socket; - private ServerSocketChannel getServerSocketChannel() throws IOException { - socket = Files.createTempDirectory(UnixSocketChannelReadWrite.class.getSimpleName()).resolve("sock"); - UnixDomainSocketAddress addr = UnixDomainSocketAddress.of(socket); - ServerSocketChannel c = ServerSocketChannel.open(StandardProtocolFamily.UNIX); - c.bind(addr); - return c; - } - @Setup(Level.Trial) public void beforeRun() throws IOException { - ssc = getServerSocketChannel(); - s1 = SocketChannel.open(ssc.getLocalAddress()); - s2 = ssc.accept(); + sscHolder = ServerUdsChannelHolder.forClass(UnixSocketChannelReadWrite.class); + s1 = SocketChannel.open(sscHolder.channel.getLocalAddress()); + s2 = sscHolder.channel.accept(); rt = new ReadThread(s2); rt.start(); @@ -78,9 +67,7 @@ public void beforeRun() throws IOException { public void afterRun() throws IOException, InterruptedException { s1.close(); s2.close(); - ssc.close(); - Files.delete(socket); - Files.delete(socket.getParent()); + sscHolder.close(); rt.join(); } From 57e7ed7d2b86c5fafc0da6534d989dd1388c1308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Tue, 22 Apr 2025 11:40:37 +0200 Subject: [PATCH 2/5] Fix copyright year --- .../org/openjdk/bench/java/net/ServerUdsChannelHolder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java b/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java index c1db1ecd79a65..e252799430eba 100644 --- a/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java +++ b/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, 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 From 0b33d9321dda7c7a7110d55dfc617288259459d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 12 May 2025 09:10:52 +0200 Subject: [PATCH 3/5] Simplify UDS clean-up logic --- .../java/net/ServerUdsChannelHolder.java | 75 ------------------- .../net/SocketChannelConnectionSetup.java | 39 +++++----- .../java/net/UnixSocketChannelReadWrite.java | 17 +++-- 3 files changed, 27 insertions(+), 104 deletions(-) delete mode 100644 test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java diff --git a/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java b/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java deleted file mode 100644 index e252799430eba..0000000000000 --- a/test/micro/org/openjdk/bench/java/net/ServerUdsChannelHolder.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2025, 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. - * - * 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. - */ -package org.openjdk.bench.java.net; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.StandardProtocolFamily; -import java.net.UnixDomainSocketAddress; -import java.nio.channels.ServerSocketChannel; -import java.nio.file.Files; -import java.nio.file.Path; - -/** - * A thread-safe utility class to create and destroy {@link ServerSocketChannel}s using unix domain sockets. - */ -final class ServerUdsChannelHolder implements AutoCloseable { - - private final Path socketFilePath; - - final ServerSocketChannel channel; - - private ServerUdsChannelHolder(String tempDirPrefix) { - try { - // Socket file will be created by `bind()`, hence, we must point to a non-existent file. - this.socketFilePath = Files.createTempDirectory(tempDirPrefix).resolve("sock"); - UnixDomainSocketAddress socketAddress = UnixDomainSocketAddress.of(socketFilePath); - this.channel = ServerSocketChannel - .open(StandardProtocolFamily.UNIX) - .bind(socketAddress); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - static ServerUdsChannelHolder forClass(Class clazz) { - return new ServerUdsChannelHolder(clazz.getSimpleName() + '-'); - } - - @Override - public void close() { - try { - channel.close(); - Files.delete(socketFilePath); - Files.delete(socketFilePath.getParent()); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - @Override - public String toString() { - return "" + socketFilePath; - } - -} diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java index f7293cd99b867..354f7fdc22302 100644 --- a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java @@ -23,8 +23,12 @@ package org.openjdk.bench.java.net; import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.*; @@ -44,44 +48,35 @@ @Fork(value = 3) public class SocketChannelConnectionSetup { - private ServerSocketChannelHolder sscHolder; + private ServerSocketChannel ssc; private SocketChannel s1, s2; - @Param({"inet", "unix"}) + @Param({"INET", "UNIX"}) private volatile String family; - private record ServerSocketChannelHolder(ServerSocketChannel channel, AutoCloseable terminator) {} - - private ServerSocketChannelHolder createServerSocketChannelHolder() throws IOException { - return switch (family) { - case "inet" -> { - ServerSocketChannel channel = ServerSocketChannel.open().bind(null); - yield new ServerSocketChannelHolder(channel, channel); - } - case "unix" -> { - ServerUdsChannelHolder holder = ServerUdsChannelHolder.forClass(SocketChannelConnectionSetup.class); - yield new ServerSocketChannelHolder(holder.channel, holder); - } - default -> throw new InternalError("unknown family: " + family); - }; - } - @Setup(Level.Trial) public void beforeRun() throws IOException { - sscHolder = createServerSocketChannelHolder(); + StandardProtocolFamily typedFamily = StandardProtocolFamily.valueOf(family); + ssc = ServerSocketChannel.open(typedFamily).bind(null); } @TearDown(Level.Trial) public void afterRun() throws Exception { - sscHolder.terminator.close(); + Path udsFilePath = ssc.getLocalAddress() instanceof UnixDomainSocketAddress udsChannel + ? udsChannel.getPath() + : null; + ssc.close(); + if (udsFilePath != null) { + Files.delete(udsFilePath); + } } @Benchmark @Measurement(iterations = 5, batchSize=200) public void test() throws IOException { - s1 = SocketChannel.open(sscHolder.channel.getLocalAddress()); - s2 = sscHolder.channel.accept(); + s1 = SocketChannel.open(ssc.getLocalAddress()); + s2 = ssc.accept(); s1.close(); s2.close(); } diff --git a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java index 7196ee0e8ad7c..cf300ad5adf2a 100644 --- a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java +++ b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java @@ -23,8 +23,11 @@ package org.openjdk.bench.java.net; import java.io.IOException; +import java.net.StandardProtocolFamily; +import java.net.UnixDomainSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; +import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.file.*; import java.util.concurrent.TimeUnit; @@ -43,18 +46,16 @@ @Fork(value = 3) public class UnixSocketChannelReadWrite { - private ServerUdsChannelHolder sscHolder; + private ServerSocketChannel ssc; private SocketChannel s1, s2; private ReadThread rt; private ByteBuffer bb = ByteBuffer.allocate(1); - private volatile Path socket; - @Setup(Level.Trial) public void beforeRun() throws IOException { - sscHolder = ServerUdsChannelHolder.forClass(UnixSocketChannelReadWrite.class); - s1 = SocketChannel.open(sscHolder.channel.getLocalAddress()); - s2 = sscHolder.channel.accept(); + ssc = ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(null); + s1 = SocketChannel.open(ssc.getLocalAddress()); + s2 = ssc.accept(); rt = new ReadThread(s2); rt.start(); @@ -67,7 +68,9 @@ public void beforeRun() throws IOException { public void afterRun() throws IOException, InterruptedException { s1.close(); s2.close(); - sscHolder.close(); + Path udsFilePath = ((UnixDomainSocketAddress) ssc.getLocalAddress()).getPath(); + ssc.close(); + Files.delete(udsFilePath); rt.join(); } From ffd31a462ef910551488a3df39a14d19f3cf9654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 12 May 2025 13:21:36 +0200 Subject: [PATCH 4/5] Improve UDS file path obtain --- .../java/net/SocketChannelConnectionSetup.java | 15 ++++++++++----- .../java/net/UnixSocketChannelReadWrite.java | 8 ++++++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java index 354f7fdc22302..acd592db29052 100644 --- a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java @@ -50,6 +50,8 @@ public class SocketChannelConnectionSetup { private ServerSocketChannel ssc; + private Path sscFilePath; + private SocketChannel s1, s2; @Param({"INET", "UNIX"}) @@ -59,16 +61,19 @@ public class SocketChannelConnectionSetup { public void beforeRun() throws IOException { StandardProtocolFamily typedFamily = StandardProtocolFamily.valueOf(family); ssc = ServerSocketChannel.open(typedFamily).bind(null); + // Record the UDS file path right after binding, as the socket may be + // closed later due to a failure, and subsequent calls to `getPath()` + // will throw. + sscFilePath = ssc.getLocalAddress() instanceof UnixDomainSocketAddress udsChannel + ? udsChannel.getPath() + : null; } @TearDown(Level.Trial) public void afterRun() throws Exception { - Path udsFilePath = ssc.getLocalAddress() instanceof UnixDomainSocketAddress udsChannel - ? udsChannel.getPath() - : null; ssc.close(); - if (udsFilePath != null) { - Files.delete(udsFilePath); + if (sscFilePath != null) { + Files.delete(sscFilePath); } } diff --git a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java index cf300ad5adf2a..ffcb2739b0de4 100644 --- a/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java +++ b/test/micro/org/openjdk/bench/java/net/UnixSocketChannelReadWrite.java @@ -47,6 +47,7 @@ public class UnixSocketChannelReadWrite { private ServerSocketChannel ssc; + private Path sscFilePath; private SocketChannel s1, s2; private ReadThread rt; private ByteBuffer bb = ByteBuffer.allocate(1); @@ -54,6 +55,10 @@ public class UnixSocketChannelReadWrite { @Setup(Level.Trial) public void beforeRun() throws IOException { ssc = ServerSocketChannel.open(StandardProtocolFamily.UNIX).bind(null); + // Record the UDS file path right after binding, as the socket may be + // closed later due to a failure, and subsequent calls to `getPath()` + // will throw. + sscFilePath = ((UnixDomainSocketAddress) ssc.getLocalAddress()).getPath(); s1 = SocketChannel.open(ssc.getLocalAddress()); s2 = ssc.accept(); @@ -68,9 +73,8 @@ public void beforeRun() throws IOException { public void afterRun() throws IOException, InterruptedException { s1.close(); s2.close(); - Path udsFilePath = ((UnixDomainSocketAddress) ssc.getLocalAddress()).getPath(); ssc.close(); - Files.delete(udsFilePath); + Files.delete(sscFilePath); rt.join(); } From d830ec8b3c91adb0788d36c17c0fa0ff109cfd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volkan=20Yaz=C4=B1c=C4=B1?= Date: Mon, 12 May 2025 21:04:02 +0200 Subject: [PATCH 5/5] Remove redundant `volatile` modifier --- .../openjdk/bench/java/net/SocketChannelConnectionSetup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java index acd592db29052..652ed134c391b 100644 --- a/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java +++ b/test/micro/org/openjdk/bench/java/net/SocketChannelConnectionSetup.java @@ -55,7 +55,7 @@ public class SocketChannelConnectionSetup { private SocketChannel s1, s2; @Param({"INET", "UNIX"}) - private volatile String family; + private String family; @Setup(Level.Trial) public void beforeRun() throws IOException {