(delta);
}
diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp
index 8404fc757f0..a03255b5cf3 100644
--- a/src/hotspot/share/utilities/macros.hpp
+++ b/src/hotspot/share/utilities/macros.hpp
@@ -548,6 +548,9 @@
#endif
#define MACOS_AARCH64_ONLY(x) MACOS_ONLY(AARCH64_ONLY(x))
+#if defined(__APPLE__) && defined(AARCH64)
+#define MACOS_AARCH64 1
+#endif
#if defined(RISCV32) || defined(RISCV64)
#define RISCV
diff --git a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp
index 71719ac8a76..5dec5062b0c 100644
--- a/src/hotspot/share/utilities/permitForbiddenFunctions.hpp
+++ b/src/hotspot/share/utilities/permitForbiddenFunctions.hpp
@@ -70,6 +70,10 @@ inline void* realloc(void* ptr, size_t size) { return ::realloc(ptr, size); }
inline char* strdup(const char* s) { return ::strdup(s); }
+MACOS_AARCH64_ONLY( \
+ inline void pthread_jit_write_protect_np(int enabled) { return ::pthread_jit_write_protect_np(enabled); } \
+)
+
END_ALLOW_FORBIDDEN_FUNCTIONS
} // namespace permit_forbidden_function
diff --git a/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java
index b645b735533..eb895d5a3a1 100644
--- a/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java
+++ b/src/java.base/aix/classes/sun/nio/ch/DefaultPollerProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 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
@@ -30,15 +30,28 @@ import java.io.IOException;
* Default PollerProvider for AIX.
*/
class DefaultPollerProvider extends PollerProvider {
- DefaultPollerProvider() { }
+ DefaultPollerProvider(Poller.Mode mode) {
+ if (mode != Poller.Mode.SYSTEM_THREADS) {
+ throw new UnsupportedOperationException();
+ }
+ super(mode);
+ }
+
+ DefaultPollerProvider() {
+ this(Poller.Mode.SYSTEM_THREADS);
+ }
@Override
Poller readPoller(boolean subPoller) throws IOException {
+ if (subPoller)
+ throw new UnsupportedOperationException();
return new PollsetPoller(true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
+ if (subPoller)
+ throw new UnsupportedOperationException();
return new PollsetPoller(false);
}
}
diff --git a/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java b/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java
index 724f14495a8..3eee39906ee 100644
--- a/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java
+++ b/src/java.base/aix/classes/sun/nio/ch/PollsetPoller.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2022, 2026, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, IBM Corp.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@@ -52,13 +52,19 @@ class PollsetPoller extends Poller {
this.pollBuffer = Pollset.allocatePollArray(MAX_EVENTS_TO_POLL);
}
+ @Override
+ void close() {
+ Pollset.pollsetDestroy(setid);
+ Pollset.freePollArray(pollBuffer);
+ }
+
@Override
int fdVal() {
return setid;
}
@Override
- void implRegister(int fd) throws IOException {
+ void implStartPoll(int fd) throws IOException {
int ret = Pollset.pollsetCtl(setid, Pollset.PS_MOD, fd, Pollset.PS_POLLPRI | event);
if (ret != 0) {
throw new IOException("Unable to register fd " + fd);
@@ -66,7 +72,7 @@ class PollsetPoller extends Poller {
}
@Override
- void implDeregister(int fd, boolean polled) {
+ void implStopPoll(int fd, boolean polled) {
int ret = Pollset.pollsetCtl(setid, Pollset.PS_DELETE, fd, 0);
assert ret == 0;
}
diff --git a/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java
index a9b169a4657..b53a90e4f7f 100644
--- a/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java
+++ b/src/java.base/linux/classes/sun/nio/ch/DefaultPollerProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -31,34 +31,34 @@ import jdk.internal.vm.ContinuationSupport;
* Default PollerProvider for Linux.
*/
class DefaultPollerProvider extends PollerProvider {
- DefaultPollerProvider() { }
+ DefaultPollerProvider(Poller.Mode mode) {
+ super(mode);
+ }
- @Override
- Poller.Mode defaultPollerMode() {
- if (ContinuationSupport.isSupported()) {
- return Poller.Mode.VTHREAD_POLLERS;
- } else {
- return Poller.Mode.SYSTEM_THREADS;
- }
+ DefaultPollerProvider() {
+ var mode = ContinuationSupport.isSupported()
+ ? Poller.Mode.VTHREAD_POLLERS
+ : Poller.Mode.SYSTEM_THREADS;
+ this(mode);
}
@Override
- int defaultReadPollers(Poller.Mode mode) {
+ int defaultReadPollers() {
int ncpus = Runtime.getRuntime().availableProcessors();
- if (mode == Poller.Mode.VTHREAD_POLLERS) {
- return Math.min(Integer.highestOneBit(ncpus), 32);
- } else {
- return Math.max(Integer.highestOneBit(ncpus / 4), 1);
- }
+ return switch (pollerMode()) {
+ case SYSTEM_THREADS -> Math.max(Integer.highestOneBit(ncpus / 4), 1);
+ case VTHREAD_POLLERS -> Math.min(Integer.highestOneBit(ncpus), 32);
+ default -> super.defaultReadPollers();
+ };
}
@Override
Poller readPoller(boolean subPoller) throws IOException {
- return new EPollPoller(subPoller, true);
+ return new EPollPoller(pollerMode(), subPoller, true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
- return new EPollPoller(subPoller, false);
+ return new EPollPoller(pollerMode(), subPoller, false);
}
}
diff --git a/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java b/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java
index cdebff7c766..5c45393bd62 100644
--- a/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java
+++ b/src/java.base/linux/classes/sun/nio/ch/EPollPoller.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -25,6 +25,8 @@
package sun.nio.ch;
import java.io.IOException;
+import java.lang.ref.Cleaner.Cleanable;
+import jdk.internal.ref.CleanerFactory;
import static sun.nio.ch.EPoll.*;
/**
@@ -38,12 +40,70 @@ class EPollPoller extends Poller {
private final int event;
private final int maxEvents;
private final long address;
+ private final EventFD eventfd; // wakeup event, used for shutdown
- EPollPoller(boolean subPoller, boolean read) throws IOException {
- this.epfd = EPoll.create();
+ // close action, and cleaner if this is subpoller
+ private final Runnable closer;
+ private final Cleanable cleaner;
+
+ EPollPoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException {
+ boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller;
+ int maxEvents = (subPoller) ? 16 : 64;
+
+ int epfd = EPoll.create();
+ long address = 0L;
+ EventFD eventfd = null;
+ try {
+ address = EPoll.allocatePollArray(maxEvents);
+
+ // register one end of the pipe with epoll to allow for wakeup
+ if (wakeable) {
+ eventfd = new EventFD();
+ IOUtil.configureBlocking(eventfd.efd(), false);
+ EPoll.ctl(epfd, EPOLL_CTL_ADD, eventfd.efd(), EPOLLIN);
+ }
+ } catch (Throwable e) {
+ FileDispatcherImpl.closeIntFD(epfd);
+ if (address != 0L) EPoll.freePollArray(address);
+ if (eventfd != null) eventfd.close();
+ throw e;
+ }
+
+ this.epfd = epfd;
this.event = (read) ? EPOLLIN : EPOLLOUT;
- this.maxEvents = (subPoller) ? 64 : 512;
- this.address = EPoll.allocatePollArray(maxEvents);
+ this.maxEvents = maxEvents;
+ this.address = address;
+ this.eventfd = eventfd;
+
+ // create action to close epoll instance, register cleaner when wakeable
+ this.closer = closer(epfd, address, eventfd);
+ if (wakeable) {
+ this.cleaner = CleanerFactory.cleaner().register(this, closer);
+ } else {
+ this.cleaner = null;
+ }
+ }
+
+ /**
+ * Returns an action to close the epoll instance and release other resources.
+ */
+ private static Runnable closer(int epfd, long address, EventFD eventfd) {
+ return () -> {
+ try {
+ FileDispatcherImpl.closeIntFD(epfd);
+ EPoll.freePollArray(address);
+ if (eventfd != null) eventfd.close();
+ } catch (IOException _) { }
+ };
+ }
+
+ @Override
+ void close() {
+ if (cleaner != null) {
+ cleaner.clean();
+ } else {
+ closer.run();
+ }
}
@Override
@@ -52,8 +112,8 @@ class EPollPoller extends Poller {
}
@Override
- void implRegister(int fdVal) throws IOException {
- // re-arm
+ void implStartPoll(int fdVal) throws IOException {
+ // re-enable if already registered but disabled (previously polled)
int err = EPoll.ctl(epfd, EPOLL_CTL_MOD, fdVal, (event | EPOLLONESHOT));
if (err == ENOENT)
err = EPoll.ctl(epfd, EPOLL_CTL_ADD, fdVal, (event | EPOLLONESHOT));
@@ -62,24 +122,36 @@ class EPollPoller extends Poller {
}
@Override
- void implDeregister(int fdVal, boolean polled) {
+ void implStopPoll(int fdVal, boolean polled) {
// event is disabled if already polled
if (!polled) {
EPoll.ctl(epfd, EPOLL_CTL_DEL, fdVal, 0);
}
}
+ @Override
+ void wakeupPoller() throws IOException {
+ if (eventfd == null) {
+ throw new UnsupportedOperationException();
+ }
+ eventfd.set();
+ }
+
@Override
int poll(int timeout) throws IOException {
int n = EPoll.wait(epfd, address, maxEvents, timeout);
+ int polled = 0;
int i = 0;
while (i < n) {
long eventAddress = EPoll.getEvent(address, i);
- int fdVal = EPoll.getDescriptor(eventAddress);
- polled(fdVal);
+ int fd = EPoll.getDescriptor(eventAddress);
+ if (eventfd == null || fd != eventfd.efd()) {
+ polled(fd);
+ polled++;
+ }
i++;
}
- return n;
+ return polled;
}
}
diff --git a/src/java.base/macosx/classes/apple/security/KeychainStore.java b/src/java.base/macosx/classes/apple/security/KeychainStore.java
index 6f70fccbb24..63dfc39afe1 100644
--- a/src/java.base/macosx/classes/apple/security/KeychainStore.java
+++ b/src/java.base/macosx/classes/apple/security/KeychainStore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2026, 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
@@ -30,6 +30,8 @@ import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.security.spec.*;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.*;
import javax.crypto.*;
@@ -114,7 +116,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
// SecCertificateRef. When we delete the key we have to delete all of the corresponding
// native objects.
static class KeyEntry {
- Date date; // the creation date of this entry
+ Instant date; // the creation instant of this entry
byte[] protectedPrivKey;
char[] password;
long keyRef; // SecKeyRef for this key
@@ -124,7 +126,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
// Trusted certificates
static class TrustedCertEntry {
- Date date; // the creation date of this entry
+ Instant date; // the creation instant of this entry
Certificate cert;
long certRef; // SecCertificateRef for this key
@@ -405,13 +407,32 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
* not exist
*/
public Date engineGetCreationDate(String alias) {
- Object entry = entries.get(alias.toLowerCase(Locale.ROOT));
+ final Instant instant = this.engineGetCreationInstant(alias);
+ if (instant == null) {
+ return null;
+ }
+ return Date.from(instant);
+ }
+
+ /**
+ * Returns the instant that the entry identified by the given alias was
+ * created.
+ *
+ * @param alias the alias name
+ *
+ * @return the instant that the entry identified by the given alias
+ * was created, or {@code null} if the given alias does not exist
+ *
+ * @since 27
+ */
+ public Instant engineGetCreationInstant(String alias) {
+ final Object entry = entries.get(alias.toLowerCase(Locale.ROOT));
if (entry != null) {
- if (entry instanceof TrustedCertEntry) {
- return new Date(((TrustedCertEntry)entry).date.getTime());
+ if (entry instanceof TrustedCertEntry trustedCertEntry) {
+ return trustedCertEntry.date;
} else {
- return new Date(((KeyEntry)entry).date.getTime());
+ return ((KeyEntry)entry).date;
}
} else {
return null;
@@ -447,7 +468,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
synchronized(entries) {
try {
KeyEntry entry = new KeyEntry();
- entry.date = new Date();
+ entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
if (key instanceof PrivateKey) {
if ((key.getFormat().equals("PKCS#8")) ||
@@ -525,7 +546,7 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
+ "EncryptedPrivateKeyInfo");
}
- entry.date = new Date();
+ entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
if ((chain != null) &&
(chain.length != 0)) {
@@ -927,9 +948,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
// Make a creation date.
if (creationDate != 0)
- tce.date = new Date(creationDate);
+ tce.date = Instant.ofEpochMilli(creationDate);
else
- tce.date = new Date();
+ tce.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
entries.put(alias.toLowerCase(Locale.ROOT), tce);
} catch (Exception e) {
@@ -952,9 +973,9 @@ abstract sealed class KeychainStore extends KeyStoreSpi {
// Make a creation date.
if (creationDate != 0)
- ke.date = new Date(creationDate);
+ ke.date = Instant.ofEpochMilli(creationDate);
else
- ke.date = new Date();
+ ke.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
// Next, create X.509 Certificate objects from the raw data. This is complicated
// because a certificate's public key may be too long for Java's default encryption strength.
diff --git a/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java b/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java
index dc32c2cd90c..6349ae503e4 100644
--- a/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java
+++ b/src/java.base/macosx/classes/sun/nio/ch/DefaultPollerProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -30,15 +30,21 @@ import java.io.IOException;
* Default PollerProvider for macOS.
*/
class DefaultPollerProvider extends PollerProvider {
- DefaultPollerProvider() { }
+ DefaultPollerProvider(Poller.Mode mode) {
+ super(mode);
+ }
+
+ DefaultPollerProvider() {
+ this(Poller.Mode.SYSTEM_THREADS);
+ }
@Override
Poller readPoller(boolean subPoller) throws IOException {
- return new KQueuePoller(subPoller, true);
+ return new KQueuePoller(pollerMode(), subPoller, true);
}
@Override
Poller writePoller(boolean subPoller) throws IOException {
- return new KQueuePoller(subPoller, false);
+ return new KQueuePoller(pollerMode(), subPoller, false);
}
}
diff --git a/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java b/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java
index 6a1c771820e..69c191913a9 100644
--- a/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java
+++ b/src/java.base/macosx/classes/sun/nio/ch/KQueuePoller.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -25,6 +25,8 @@
package sun.nio.ch;
import java.io.IOException;
+import java.lang.ref.Cleaner.Cleanable;
+import jdk.internal.ref.CleanerFactory;
import static sun.nio.ch.KQueue.*;
/**
@@ -36,11 +38,77 @@ class KQueuePoller extends Poller {
private final int maxEvents;
private final long address;
- KQueuePoller(boolean subPoller, boolean read) throws IOException {
- this.kqfd = KQueue.create();
+ // file descriptors used for wakeup during shutdown
+ private final int fd0;
+ private final int fd1;
+
+ // close action, and cleaner if this is subpoller
+ private final Runnable closer;
+ private final Cleanable cleaner;
+
+ KQueuePoller(Poller.Mode mode, boolean subPoller, boolean read) throws IOException {
+ boolean wakeable = (mode == Mode.POLLER_PER_CARRIER) && subPoller;
+ int maxEvents = (subPoller) ? 16 : 64;
+
+ int kqfd = KQueue.create();
+ long address = 0L;
+ int fd0 = -1;
+ int fd1 = -1;
+ try {
+ address = KQueue.allocatePollArray(maxEvents);
+
+ // register one end of the pipe with kqueue to allow for wakeup
+ if (wakeable) {
+ long fds = IOUtil.makePipe(false);
+ fd0 = (int) (fds >>> 32);
+ fd1 = (int) fds;
+ KQueue.register(kqfd, fd0, EVFILT_READ, EV_ADD);
+ }
+ } catch (Throwable e) {
+ FileDispatcherImpl.closeIntFD(kqfd);
+ if (address != 0L) KQueue.freePollArray(address);
+ if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0);
+ if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1);
+ throw e;
+ }
+
+ this.kqfd = kqfd;
this.filter = (read) ? EVFILT_READ : EVFILT_WRITE;
- this.maxEvents = (subPoller) ? 64 : 512;
- this.address = KQueue.allocatePollArray(maxEvents);
+ this.maxEvents = maxEvents;
+ this.address = address;
+ this.fd0 = fd0;
+ this.fd1 = fd1;
+
+ // create action to close kqueue, register cleaner when wakeable
+ this.closer = closer(kqfd, address, fd0, fd1);
+ if (wakeable) {
+ this.cleaner = CleanerFactory.cleaner().register(this, closer);
+ } else {
+ this.cleaner = null;
+ }
+ }
+
+ /**
+ * Returns an action to close the kqueue and release other resources.
+ */
+ private static Runnable closer(int kqfd, long address, int fd0, int fd1) {
+ return () -> {
+ try {
+ FileDispatcherImpl.closeIntFD(kqfd);
+ KQueue.freePollArray(address);
+ if (fd0 >= 0) FileDispatcherImpl.closeIntFD(fd0);
+ if (fd1 >= 0) FileDispatcherImpl.closeIntFD(fd1);
+ } catch (IOException _) { }
+ };
+ }
+
+ @Override
+ void close() {
+ if (cleaner != null) {
+ cleaner.clean();
+ } else {
+ closer.run();
+ }
}
@Override
@@ -49,30 +117,42 @@ class KQueuePoller extends Poller {
}
@Override
- void implRegister(int fdVal) throws IOException {
+ void implStartPoll(int fdVal) throws IOException {
int err = KQueue.register(kqfd, fdVal, filter, (EV_ADD|EV_ONESHOT));
if (err != 0)
throw new IOException("kevent failed: " + err);
}
@Override
- void implDeregister(int fdVal, boolean polled) {
+ void implStopPoll(int fdVal, boolean polled) {
// event was deleted if already polled
if (!polled) {
KQueue.register(kqfd, fdVal, filter, EV_DELETE);
}
}
+ @Override
+ void wakeupPoller() throws IOException {
+ if (fd1 < 0) {
+ throw new UnsupportedOperationException();
+ }
+ IOUtil.write1(fd1, (byte)0);
+ }
+
@Override
int poll(int timeout) throws IOException {
int n = KQueue.poll(kqfd, address, maxEvents, timeout);
+ int polled = 0;
int i = 0;
while (i < n) {
long keventAddress = KQueue.getEvent(address, i);
int fdVal = KQueue.getDescriptor(keventAddress);
- polled(fdVal);
+ if (fdVal != fd0) {
+ polled(fdVal);
+ polled++;
+ }
i++;
}
- return n;
+ return polled;
}
}
diff --git a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
index ad98653b9c2..1e45a4e6a20 100644
--- a/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
+++ b/src/java.base/share/classes/com/sun/crypto/provider/JceKeyStore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -29,6 +29,8 @@ import sun.security.util.Debug;
import sun.security.util.IOUtils;
import java.io.*;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
import java.util.*;
import java.security.DigestInputStream;
import java.security.DigestOutputStream;
@@ -70,14 +72,14 @@ public final class JceKeyStore extends KeyStoreSpi {
// Private key and supporting certificate chain
private static final class PrivateKeyEntry {
- Date date; // the creation date of this entry
+ Instant date; // the creation date of this entry
byte[] protectedKey;
Certificate[] chain;
}
// Secret key
private static final class SecretKeyEntry {
- Date date; // the creation date of this entry
+ Instant date; // the creation date of this entry
SealedObject sealedKey;
// Maximum possible length of sealedKey. Used to detect malicious
@@ -89,7 +91,7 @@ public final class JceKeyStore extends KeyStoreSpi {
// Trusted certificate
private static final class TrustedCertEntry {
- Date date; // the creation date of this entry
+ Instant date; // the creation date of this entry
Certificate cert;
}
@@ -213,23 +215,38 @@ public final class JceKeyStore extends KeyStoreSpi {
* not exist
*/
public Date engineGetCreationDate(String alias) {
- Date date = null;
+ final Instant instant = this.engineGetCreationInstant(alias);
+ if (instant == null) {
+ return null;
+ }
+ return Date.from(instant);
+ }
- Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
+ /**
+ * Returns the instant that the entry identified by the given alias was
+ * created.
+ *
+ * @param alias the alias name
+ *
+ * @return the instant that the entry identified by the given alias
+ * was created, or {@code null} if the given alias does not exist
+ *
+ * @since 27
+ */
+ public Instant engineGetCreationInstant(String alias) {
+ final Object entry = entries.get(alias.toLowerCase(Locale.ENGLISH));
if (entry != null) {
- // We have to create a new instance of java.util.Date because
- // dates are not immutable
if (entry instanceof TrustedCertEntry) {
- date = new Date(((TrustedCertEntry)entry).date.getTime());
+ return ((TrustedCertEntry)entry).date;
} else if (entry instanceof PrivateKeyEntry) {
- date = new Date(((PrivateKeyEntry)entry).date.getTime());
+ return ((PrivateKeyEntry)entry).date;
} else {
- date = new Date(((SecretKeyEntry)entry).date.getTime());
+ return ((SecretKeyEntry)entry).date;
}
+ } else {
+ return null;
}
-
- return date;
}
/**
@@ -264,7 +281,7 @@ public final class JceKeyStore extends KeyStoreSpi {
if (key instanceof PrivateKey) {
PrivateKeyEntry entry = new PrivateKeyEntry();
- entry.date = new Date();
+ entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
// protect the private key
entry.protectedKey = keyProtector.protect((PrivateKey)key);
@@ -282,7 +299,7 @@ public final class JceKeyStore extends KeyStoreSpi {
} else {
SecretKeyEntry entry = new SecretKeyEntry();
- entry.date = new Date();
+ entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
// seal and store the key
entry.sealedKey = keyProtector.seal(key);
@@ -325,7 +342,7 @@ public final class JceKeyStore extends KeyStoreSpi {
// We assume it's a private key, because there is no standard
// (ASN.1) encoding format for wrapped secret keys
PrivateKeyEntry entry = new PrivateKeyEntry();
- entry.date = new Date();
+ entry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
entry.protectedKey = key.clone();
if ((chain != null) &&
@@ -370,7 +387,7 @@ public final class JceKeyStore extends KeyStoreSpi {
TrustedCertEntry trustedCertEntry = new TrustedCertEntry();
trustedCertEntry.cert = cert;
- trustedCertEntry.date = new Date();
+ trustedCertEntry.date = Instant.now().truncatedTo(ChronoUnit.MILLIS);
entries.put(alias.toLowerCase(Locale.ENGLISH), trustedCertEntry);
}
}
@@ -588,7 +605,7 @@ public final class JceKeyStore extends KeyStoreSpi {
dos.writeUTF(alias);
// write the (entry creation) date
- dos.writeLong(pentry.date.getTime());
+ dos.writeLong(pentry.date.toEpochMilli());
// write the protected private key
dos.writeInt(pentry.protectedKey.length);
@@ -618,7 +635,9 @@ public final class JceKeyStore extends KeyStoreSpi {
dos.writeUTF(alias);
// write the (entry creation) date
- dos.writeLong(((TrustedCertEntry)entry).date.getTime());
+ dos.writeLong(
+ ((TrustedCertEntry)entry).date.toEpochMilli()
+ );
// write the trusted certificate
encoded = ((TrustedCertEntry)entry).cert.getEncoded();
@@ -635,7 +654,9 @@ public final class JceKeyStore extends KeyStoreSpi {
dos.writeUTF(alias);
// write the (entry creation) date
- dos.writeLong(((SecretKeyEntry)entry).date.getTime());
+ dos.writeLong(
+ ((SecretKeyEntry)entry).date.toEpochMilli()
+ );
// write the sealed key
oos = new ObjectOutputStream(dos);
@@ -753,7 +774,7 @@ public final class JceKeyStore extends KeyStoreSpi {
alias = dis.readUTF();
// read the (entry creation) date
- entry.date = new Date(dis.readLong());
+ entry.date = Instant.ofEpochMilli(dis.readLong());
// read the private key
entry.protectedKey = IOUtils.readExactlyNBytes(dis, dis.readInt());
@@ -798,7 +819,7 @@ public final class JceKeyStore extends KeyStoreSpi {
alias = dis.readUTF();
// read the (entry creation) date
- entry.date = new Date(dis.readLong());
+ entry.date = Instant.ofEpochMilli(dis.readLong());
// read the trusted certificate
if (xVersion == 2) {
@@ -832,7 +853,7 @@ public final class JceKeyStore extends KeyStoreSpi {
alias = dis.readUTF();
// read the (entry creation) date
- entry.date = new Date(dis.readLong());
+ entry.date = Instant.ofEpochMilli(dis.readLong());
// read the sealed key
try {
diff --git a/src/java.base/share/classes/java/lang/FdLibm.java b/src/java.base/share/classes/java/lang/FdLibm.java
index 73e1da46af4..8e75f8f6994 100644
--- a/src/java.base/share/classes/java/lang/FdLibm.java
+++ b/src/java.base/share/classes/java/lang/FdLibm.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -3508,4 +3508,103 @@ final class FdLibm {
return iz;
}
}
+
+ /**
+ * Return the Inverse Hyperbolic Sine of x
+ *
+ * Method :
+ *
+ *
+ * asinh(x) is defined so that asinh(sinh(alpha)) = alpha, -∞ < alpha < ∞
+ * and sinh(asinh(x)) = x, -∞ < x < ∞
+ * It can be written as asinh(x) = sign(x) * ln(|x| + sqrt(x^2 + 1)), -∞ < x < ∞
+ * 1. Replace x by |x| as the function is odd.
+ * 2.
+ * asinh(x) := x, if 1+x^2 = 1,
+ * := sign(x)*(log(x)+ln2)) for large |x|, else
+ * := sign(x)*log(2|x|+1/(|x|+sqrt(x*x+1))) if|x|>2, else
+ * := sign(x)*log1p(|x| + x^2/(1 + sqrt(1+x^2)))
+ *
+ *
+ *
+ * Special cases:
+ * only asinh(±0)=±0 is exact for finite x.
+ * asinh(NaN) is NaN
+ * asinh(±∞) is ±∞
+ */
+ static final class Asinh {
+ private static final double ln2 = 6.93147180559945286227e-01;
+ private static final double huge = 1.0e300;
+
+ static double compute(double x) {
+ double t, w;
+ int hx, ix;
+ hx = __HI(x);
+ ix = hx & 0x7fff_ffff;
+ if (ix >= 0x7ff0_0000) {
+ return x + x; // x is inf or NaN
+ }
+ if (ix < 0x3e30_0000) { // |x| < 2**-28
+ if (huge + x > 1.0) {
+ return x; // return x inexact except 0
+ }
+ }
+ if (ix > 0x41b0_0000) { // |x| > 2**28
+ w = Log.compute(Math.abs(x)) + ln2;
+ } else if (ix > 0x4000_0000) { // 2**28 > |x| > 2.0
+ t = Math.abs(x);
+ w = Log.compute(2.0 * t + 1.0 / (Sqrt.compute(x * x + 1.0) + t));
+ } else { // 2.0 > |x| > 2**-28
+ t = x * x;
+ w = Log1p.compute(Math.abs(x) + t / (1.0 + Sqrt.compute(1.0 + t)));
+ }
+ return hx > 0 ? w : -w;
+ }
+ }
+
+ /**
+ * Return the Inverse Hyperbolic Cosine of x
+ *
+ * Method :
+ *
+ *
+ * acosh(x) is defined so that acosh(cosh(alpha)) = alpha, -∞ < alpha < ∞
+ * and cosh(acosh(x)) = x, 1 <= x < ∞.
+ * It can be written as acosh(x) = log(x + sqrt(x^2 - 1)), 1 <= x < ∞.
+ * acosh(x) := log(x)+ln2, if x is large; else
+ * := log(2x-1/(sqrt(x*x-1)+x)) if x>2; else
+ * := log1p(t+sqrt(2.0*t+t*t)); where t=x-1.
+ *
+ *
+ *
+ * Special cases:
+ * acosh(x) is NaN with signal if x < 1.
+ * acosh(NaN) is NaN without signal.
+ */
+ static final class Acosh {
+ private static final double ln2 = 6.93147180559945286227e-01;
+
+ static double compute(double x) {
+ double t;
+ int hx;
+ hx = __HI(x);
+ if (hx < 0x3ff0_0000) { // x < 1 */
+ return (x - x) / (x - x);
+ } else if (hx >= 0x41b0_0000) { // x > 2**28
+ if (hx >= 0x7ff0_0000) { // x is inf of NaN
+ return x + x;
+ } else {
+ return Log.compute(x) + ln2; // acosh(huge) = log(2x)
+ }
+ } else if (((hx - 0x3ff0_0000) | __LO(x)) == 0) {
+ return 0.0; // acosh(1) = 0
+ } else if (hx > 0x4000_0000) { // 2**28 > x > 2
+ t = x * x;
+ return Log.compute(2.0 * x - 1.0 / (x + Sqrt.compute(t - 1.0)));
+ } else { // 1< x <2
+ t = x - 1.0;
+ return Log1p.compute(t + Sqrt.compute(2.0 * t + t * t));
+ }
+ }
+ }
}
diff --git a/src/java.base/share/classes/java/lang/Math.java b/src/java.base/share/classes/java/lang/Math.java
index 55659bed57b..4f729fe82cb 100644
--- a/src/java.base/share/classes/java/lang/Math.java
+++ b/src/java.base/share/classes/java/lang/Math.java
@@ -108,11 +108,11 @@ import static java.lang.Double.*;
* sin}, {@link cos cos}, {@link tan tan}, {@link asin asin}, {@link
* acos acos}, {@link atan atan}, {@link exp exp}, {@link expm1
* expm1}, {@link log log}, {@link log10 log10}, {@link log1p log1p},
- * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link
- * hypot hypot}, and {@link pow pow}. (The {@link sqrt sqrt}
- * operation is a required part of IEEE 754 from a different section
- * of the standard.) The special case behavior of the recommended
- * operations generally follows the guidance of the IEEE 754
+ * {@link sinh sinh}, {@link cosh cosh}, {@link tanh tanh}, {@link asinh asinh},
+ * {@link acosh acosh}, {@link hypot hypot}, and {@link pow pow}.
+ * (The {@link sqrt sqrt} operation is a required part of IEEE 754
+ * from a different section of the standard.) The special case behavior
+ * of the recommended operations generally follows the guidance of the IEEE 754
* standard. However, the {@code pow} method defines different
* behavior for some arguments, as noted in its {@linkplain pow
* specification}. The IEEE 754 standard defines its operations to be
@@ -2758,6 +2758,62 @@ public final class Math {
return StrictMath.tanh(x);
}
+ /**
+ * Returns the inverse hyperbolic sine of a {@code double} value.
+ * The inverse hyperbolic sine of x is defined to be the function such that
+ * asinh({@linkplain Math#sinh sinh(x)}) = x for any x.
+ * Note that both domain and range of the exact asinh are unrestricted.
+ * Special cases:
+ *
+ *
+ * - If the argument is zero, then the result is a zero with the
+ * same sign as the argument.
+ *
+ *
- If the argument is infinity, then the result is
+ * infinity with the same sign as the argument.
+ *
+ *
- If the argument is NaN, then the result is NaN.
+ *
+ *
+ *
+ * The computed result must be within 2.5 ulps of the exact result.
+ * @param x The number whose inverse hyperbolic sine is to be returned.
+ * @return The inverse hyperbolic sine of {@code x}.
+ * @since 27
+ */
+ public static double asinh(double x) {
+ return StrictMath.asinh(x);
+ }
+
+
+
+ /**
+ * Returns the inverse hyperbolic cosine of a {@code double} value.
+ * The inverse hyperbolic cosine of x is defined to be the function such that
+ * acosh({@linkplain Math#cosh cosh(x)}) = x for any x >= 0.
+ * Note that range of the exact acosh(x) is >= 0.
+ *
Special cases:
+ *
+ *
+ * - If the argument is positive infinity, then the result is
+ * positive infinity
+ *
+ *
- If the argument less than 1, then the result is NaN.
+ *
+ *
- If the argument is NaN, then the result is NaN.
+ *
+ *
- If the argument is {@code 1.0}, then the result is positive zero.
+ *
+ *
+ * The computed result must be within 2.5 ulps of the exact result.
+ * @param x The number whose inverse hyperbolic cosine is to be returned.
+ * @return The inverse hyperbolic cosine of {@code x}.
+ * @since 27
+ */
+ public static double acosh(double x) {
+ return StrictMath.acosh(x);
+ }
+
/**
* Returns sqrt(x2 +y2)
* without intermediate overflow or underflow.
diff --git a/src/java.base/share/classes/java/lang/ProcessBuilder.java b/src/java.base/share/classes/java/lang/ProcessBuilder.java
index d461818eedf..bc92ea4ee82 100644
--- a/src/java.base/share/classes/java/lang/ProcessBuilder.java
+++ b/src/java.base/share/classes/java/lang/ProcessBuilder.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, 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
@@ -930,6 +930,12 @@ public final class ProcessBuilder
* command interpreters, or the standard C library function
* {@code system()}.
*
+ * @implNote
+ * When the process is {@link #start started},
+ * if {#code System.out} and/or {#code System.err} have been
+ * closed in the current process, the corresponding output
+ * in the subprocess will be discarded.
+ *
* @return this process builder
* @since 1.7
*/
diff --git a/src/java.base/share/classes/java/lang/StrictMath.java b/src/java.base/share/classes/java/lang/StrictMath.java
index 499fce73aee..9421b41620b 100644
--- a/src/java.base/share/classes/java/lang/StrictMath.java
+++ b/src/java.base/share/classes/java/lang/StrictMath.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2026, 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
@@ -76,7 +76,8 @@ import jdk.internal.vm.annotation.IntrinsicCandidate;
* {@code exp}, {@code log}, {@code log10},
* {@code cbrt}, {@code atan2}, {@code pow},
* {@code sinh}, {@code cosh}, {@code tanh},
- * {@code hypot}, {@code expm1}, and {@code log1p}.
+ * {@code asinh}, {@code acosh}, {@code hypot},
+ * {@code expm1}, and {@code log1p}.
*
*
* The platform uses signed two's complement integer arithmetic with
@@ -2170,6 +2171,57 @@ public final class StrictMath {
return FdLibm.Tanh.compute(x);
}
+ /**
+ * Returns the inverse hyperbolic sine of a {@code double} value.
+ * The inverse hyperbolic sine of x is defined to be the function such that
+ * asinh({@linkplain Math#sinh sinh(x)}) = x for any x.
+ * Note that both domain and range of the exact asinh are unrestricted.
+ *
Special cases:
+ *
+ *
+ * - If the argument is zero, then the result is a zero with the
+ * same sign as the argument.
+ *
+ *
- If the argument is infinity, then the result is
+ * infinity with the same sign as the argument.
+ *
+ *
- If the argument is NaN, then the result is NaN.
+ *
+ *
+ * @param x The number whose inverse hyperbolic sine is to be returned.
+ * @return The inverse hyperbolic sine of {@code x}.
+ * @since 27
+ */
+ public static double asinh(double x) {
+ return FdLibm.Asinh.compute(x);
+ }
+
+ /**
+ * Returns the inverse hyperbolic cosine of a {@code double} value.
+ * The inverse hyperbolic cosine of x is defined to be the function such that
+ * acosh({@linkplain Math#cosh cosh(x)}) = x for any x >= 0.
+ * Note that range of the exact acosh(x) is >= 0.
+ * Special cases:
+ *
+ *
+ * - If the argument is positive infinity, then the result is
+ * positive infinity
+ *
+ *
- If the argument less than {@code 1.0}, then the result is NaN.
+ *
+ *
- If the argument is NaN, then the result is NaN.
+ *
+ *
- If the argument is {@code 1.0}, then the result is positive zero.
+ *
+ *
+ * @param x The number whose inverse hyperbolic cosine is to be returned.
+ * @return The inverse hyperbolic cosine of {@code x}.
+ * @since 27
+ */
+ public static double acosh(double x) {
+ return FdLibm.Acosh.compute(x);
+ }
+
/**
* Returns sqrt(x2 +y2)
* without intermediate overflow or underflow.
diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index bd684fab629..2f772e4d065 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -2277,6 +2277,14 @@ public final class System {
return Thread.scopedValueBindings();
}
+ public long nativeThreadID(Thread thread) {
+ return thread.nativeThreadID();
+ }
+
+ public void setThreadNativeID(long id) {
+ Thread.currentThread().setNativeThreadID(id);
+ }
+
public Continuation getContinuation(Thread thread) {
return thread.getContinuation();
}
@@ -2311,7 +2319,7 @@ public final class System {
if (thread instanceof BaseVirtualThread vthread) {
vthread.unpark();
} else {
- throw new WrongThreadException();
+ throw new IllegalArgumentException();
}
}
diff --git a/src/java.base/share/classes/java/lang/Thread.java b/src/java.base/share/classes/java/lang/Thread.java
index bb1292b374a..57d28aca5f4 100644
--- a/src/java.base/share/classes/java/lang/Thread.java
+++ b/src/java.base/share/classes/java/lang/Thread.java
@@ -286,6 +286,9 @@ public class Thread implements Runnable {
volatile boolean daemon;
volatile int threadStatus;
+ // Used by NativeThread for signalling
+ @Stable long nativeThreadID;
+
// This map is maintained by the ThreadLocal class
ThreadLocal.ThreadLocalMap terminatingThreadLocals;
@@ -312,6 +315,14 @@ public class Thread implements Runnable {
holder.terminatingThreadLocals = map;
}
+ long nativeThreadID() {
+ return holder.nativeThreadID;
+ }
+
+ void setNativeThreadID(long id) {
+ holder.nativeThreadID = id;
+ }
+
/*
* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class.
diff --git a/src/java.base/share/classes/java/security/KeyStore.java b/src/java.base/share/classes/java/security/KeyStore.java
index 8f3d4ba29fd..83414082cab 100644
--- a/src/java.base/share/classes/java/security/KeyStore.java
+++ b/src/java.base/share/classes/java/security/KeyStore.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2026, 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
@@ -30,6 +30,7 @@ import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.security.cert.CertificateException;
import java.security.spec.AlgorithmParameterSpec;
+import java.time.Instant;
import java.util.*;
import javax.crypto.SecretKey;
@@ -1182,6 +1183,9 @@ public class KeyStore {
/**
* Returns the creation date of the entry identified by the given alias.
+ *
+ * It is recommended to use the {@link #getCreationInstant(String)}
+ * method instead.
*
* @param alias the alias name
*
@@ -1200,6 +1204,32 @@ public class KeyStore {
return keyStoreSpi.engineGetCreationDate(alias);
}
+
+ /**
+ * Returns the instant that the entry identified by the given alias was
+ * created.
+ *
+ * @param alias the alias name
+ *
+ * @return the instant that the entry identified by the given alias
+ * was created, or {@code null} if the given alias does not exist
+ *
+ * @throws KeyStoreException if the keystore has not been initialized
+ * (loaded)
+ *
+ * @since 27
+ */
+ public final Instant getCreationInstant(String alias)
+ throws KeyStoreException
+ {
+ if (!initialized) {
+ throw new KeyStoreException("Uninitialized keystore");
+ }
+ return keyStoreSpi.engineGetCreationInstant(alias);
+ }
+
+
+
/**
* Assigns the given key to the given alias, protecting it with the given
* password.
diff --git a/src/java.base/share/classes/java/security/KeyStoreSpi.java b/src/java.base/share/classes/java/security/KeyStoreSpi.java
index 0b0aa8adf19..5c60f44e4e9 100644
--- a/src/java.base/share/classes/java/security/KeyStoreSpi.java
+++ b/src/java.base/share/classes/java/security/KeyStoreSpi.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1998, 2022, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, 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
@@ -26,6 +26,7 @@
package java.security;
import java.io.*;
+import java.time.Instant;
import java.util.*;
import java.security.KeyStore.*;
@@ -127,6 +128,29 @@ public abstract class KeyStoreSpi {
*/
public abstract Date engineGetCreationDate(String alias);
+ /**
+ * Returns the instant that the entry identified by the given alias was
+ * created.
+ *
+ * @apiNote Subclasses should override this method to directly return an
+ * instant.
+ *
+ * @implSpec
+ * The default implementation calls {@code engineGetCreationDate(alias)}
+ * and returns the output as an {@code Instant} value.
+ *
+ * @param alias the alias name
+ *
+ * @return the instant that the entry identified by the given alias
+ * was created, or {@code null} if the given alias does not exist
+ *
+ * @since 27
+ */
+ public Instant engineGetCreationInstant(String alias) {
+ final Date date = engineGetCreationDate(alias);
+ return date == null ? null : date.toInstant();
+ }
+
/**
* Assigns the given key to the given alias, protecting it with the given
* password.
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
index 4ae1905aa10..9d980c3ba3b 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
@@ -581,6 +581,16 @@ public interface JavaLangAccess {
*/
Object scopedValueBindings();
+ /**
+ * Returns the native thread ID for the given platform thread or 0 if not set.
+ */
+ long nativeThreadID(Thread thread);
+
+ /**
+ * Sets the native thread ID for the current platform thread.
+ */
+ void setThreadNativeID(long id);
+
/**
* Returns the innermost mounted continuation
*/
diff --git a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
index f75d67adbbb..235797f66e3 100644
--- a/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
+++ b/src/java.base/share/classes/jdk/internal/foreign/AbstractMemorySegmentImpl.java
@@ -551,6 +551,7 @@ public abstract sealed class AbstractMemorySegmentImpl
unsafeGetOffset() == that.unsafeGetOffset();
}
+ @ForceInline
@Override
public String getString(long offset, Charset charset, long byteLength) {
Utils.checkNonNegativeArgument(byteLength, "byteLength");
diff --git a/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java b/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java
index d30d18df9d6..45738550f59 100644
--- a/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java
+++ b/src/java.base/share/classes/sun/net/www/protocol/jar/URLJarFile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2026, 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
@@ -34,8 +34,6 @@ import java.util.*;
import java.util.jar.*;
import java.util.zip.ZipFile;
import java.util.zip.ZipEntry;
-import java.security.CodeSigner;
-import java.security.cert.Certificate;
import sun.net.www.ParseUtil;
/* URL jar file is a common JarFile subtype used for JarURLConnection */
@@ -165,13 +163,12 @@ public class URLJarFile extends JarFile {
}
private class URLJarFileEntry extends JarEntry {
- private final JarEntry je;
URLJarFileEntry(JarEntry je) {
super(je);
- this.je = je;
}
+ @Override
public Attributes getAttributes() throws IOException {
if (URLJarFile.this.isSuperMan()) {
Map e = URLJarFile.this.superEntries;
@@ -183,16 +180,6 @@ public class URLJarFile extends JarFile {
}
return null;
}
-
- public java.security.cert.Certificate[] getCertificates() {
- Certificate[] certs = je.getCertificates();
- return certs == null? null: certs.clone();
- }
-
- public CodeSigner[] getCodeSigners() {
- CodeSigner[] csg = je.getCodeSigners();
- return csg == null? null: csg.clone();
- }
}
public interface URLJarFileCloseController {
diff --git a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
index afb312ed722..f5002e8b716 100644
--- a/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/DatagramChannelImpl.java
@@ -134,9 +134,9 @@ class DatagramChannelImpl
private static final int ST_CLOSED = 3;
private int state;
- // IDs of native threads doing reads and writes, for signalling
- private long readerThread;
- private long writerThread;
+ // Threads doing reads and writes, for signalling
+ private Thread readerThread;
+ private Thread writerThread;
// Local and remote (connected) address
private InetSocketAddress localAddress;
@@ -523,7 +523,7 @@ class DatagramChannelImpl
if (localAddress == null)
bindInternal(null);
if (blocking)
- readerThread = NativeThread.current();
+ readerThread = NativeThread.threadToSignal();
}
return remote;
}
@@ -538,7 +538,7 @@ class DatagramChannelImpl
{
if (blocking) {
synchronized (stateLock) {
- readerThread = 0;
+ readerThread = null;
if (state == ST_CLOSING) {
tryFinishClose();
}
@@ -1030,7 +1030,7 @@ class DatagramChannelImpl
if (localAddress == null)
bindInternal(null);
if (blocking)
- writerThread = NativeThread.current();
+ writerThread = NativeThread.threadToSignal();
}
return remote;
}
@@ -1045,7 +1045,7 @@ class DatagramChannelImpl
{
if (blocking) {
synchronized (stateLock) {
- writerThread = 0;
+ writerThread = null;
if (state == ST_CLOSING) {
tryFinishClose();
}
@@ -1714,7 +1714,7 @@ class DatagramChannelImpl
*/
private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
- if ((readerThread == 0) && (writerThread == 0) && !isRegistered()) {
+ if ((readerThread == null) && (writerThread == null) && !isRegistered()) {
state = ST_CLOSED;
try {
// close socket
diff --git a/src/java.base/share/classes/sun/nio/ch/IOUtil.java b/src/java.base/share/classes/sun/nio/ch/IOUtil.java
index 45f8cb2e588..df6677ab94d 100644
--- a/src/java.base/share/classes/sun/nio/ch/IOUtil.java
+++ b/src/java.base/share/classes/sun/nio/ch/IOUtil.java
@@ -587,9 +587,11 @@ public final class IOUtil {
*/
static native int drain1(int fd) throws IOException;
- public static native void configureBlocking(FileDescriptor fd,
- boolean blocking)
- throws IOException;
+ static native void configureBlocking(int fd, boolean blocking) throws IOException;
+
+ public static void configureBlocking(FileDescriptor fd, boolean blocking) throws IOException {
+ configureBlocking(fdVal(fd), blocking);
+ }
public static native int fdVal(FileDescriptor fd);
diff --git a/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java b/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java
index 9b65310784a..63009b407ac 100644
--- a/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java
+++ b/src/java.base/share/classes/sun/nio/ch/NativeDispatcher.java
@@ -27,8 +27,6 @@ package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
-import jdk.internal.access.JavaIOFileDescriptorAccess;
-import jdk.internal.access.SharedSecrets;
/**
* Allows different platforms to call different native methods
@@ -36,7 +34,6 @@ import jdk.internal.access.SharedSecrets;
*/
abstract class NativeDispatcher {
- private static final JavaIOFileDescriptorAccess JIOFDA = SharedSecrets.getJavaIOFileDescriptorAccess();
abstract int read(FileDescriptor fd, long address, int len)
throws IOException;
@@ -78,12 +75,17 @@ abstract class NativeDispatcher {
* if a platform thread is blocked on the file descriptor then the file descriptor is
* dup'ed to a special fd and the thread signalled so that the syscall fails with EINTR.
*/
- final void preClose(FileDescriptor fd, long reader, long writer) throws IOException {
- if (NativeThread.isVirtualThread(reader) || NativeThread.isVirtualThread(writer)) {
- int fdVal = JIOFDA.get(fd);
- Poller.stopPoll(fdVal);
+ final void preClose(FileDescriptor fd, Thread reader, Thread writer) throws IOException {
+ if (reader != null && reader.isVirtual()) {
+ NativeThread.signal(reader); // unparks virtual thread
+ reader = null;
}
- if (NativeThread.isNativeThread(reader) || NativeThread.isNativeThread(writer)) {
+ if (writer != null && writer.isVirtual()) {
+ NativeThread.signal(writer); // unparks virtual thread
+ writer = null;
+ }
+ // dup2 and signal platform threads
+ if (reader != null || writer != null) {
implPreClose(fd, reader, writer);
}
}
@@ -92,8 +94,7 @@ abstract class NativeDispatcher {
* This method does nothing by default. On Unix systems the file descriptor is dup'ed
* to a special fd and native threads signalled.
*/
-
- void implPreClose(FileDescriptor fd, long reader, long writer) throws IOException {
+ void implPreClose(FileDescriptor fd, Thread reader, Thread writer) throws IOException {
// Do nothing by default; this is only needed on Unix
}
diff --git a/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java b/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java
index 079291572a8..c5423141789 100644
--- a/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java
+++ b/src/java.base/share/classes/sun/nio/ch/NativeThreadSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 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
@@ -31,9 +31,9 @@ class NativeThreadSet {
private static final int OTHER_THREAD_INDEX = -99;
private final int initialCapacity;
- private long[] threads; // array of thread handles, created lazily
- private int used; // number of thread handles in threads array
- private int otherThreads; // count of threads without a native thread handle
+ private Thread[] threads; // array of platform threads, created lazily
+ private int used; // number of elements in threads array
+ private int otherThreads; // additional threads that can't be signalled
private boolean waitingToEmpty;
NativeThreadSet(int n) {
@@ -45,28 +45,28 @@ class NativeThreadSet {
* it can efficiently be removed later.
*/
int add() {
- long th = NativeThread.current();
synchronized (this) {
- if (!NativeThread.isNativeThread(th)) {
+ final Thread t = NativeThread.threadToSignal();
+ if (t == null || t.isVirtual()) {
otherThreads++;
return OTHER_THREAD_INDEX;
}
- // add native thread handle to array, creating or growing array if needed
+ // add platform threads to array, creating or growing array if needed
int start = 0;
if (threads == null) {
- threads = new long[initialCapacity];
+ threads = new Thread[initialCapacity];
} else if (used >= threads.length) {
int on = threads.length;
int nn = on * 2;
- long[] nthreads = new long[nn];
+ Thread[] nthreads = new Thread[nn];
System.arraycopy(threads, 0, nthreads, 0, on);
threads = nthreads;
start = on;
}
for (int i = start; i < threads.length; i++) {
- if (threads[i] == 0) {
- threads[i] = th;
+ if (threads[i] == null) {
+ threads[i] = t;
used++;
return i;
}
@@ -81,8 +81,7 @@ class NativeThreadSet {
void remove(int i) {
synchronized (this) {
if (i >= 0) {
- assert threads[i] == NativeThread.current();
- threads[i] = 0;
+ threads[i] = null;
used--;
} else if (i == OTHER_THREAD_INDEX) {
otherThreads--;
@@ -104,9 +103,9 @@ class NativeThreadSet {
while (used > 0 || otherThreads > 0) {
int u = used, i = 0;
while (u > 0 && i < threads.length) {
- long th = threads[i];
- if (th != 0) {
- NativeThread.signal(th);
+ Thread t = threads[i];
+ if (t != null) {
+ NativeThread.signal(t);
u--;
}
i++;
diff --git a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java
index 01f894be227..58b3bc7aaba 100644
--- a/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java
+++ b/src/java.base/share/classes/sun/nio/ch/NioSocketImpl.java
@@ -103,8 +103,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
private volatile boolean nonBlocking;
// used by connect/read/write/accept, protected by stateLock
- private long readerThread;
- private long writerThread;
+ private Thread readerThread;
+ private Thread writerThread;
// used when SO_REUSEADDR is emulated, protected by stateLock
private boolean isReuseAddress;
@@ -218,7 +218,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
private FileDescriptor beginRead() throws SocketException {
synchronized (stateLock) {
ensureOpenAndConnected();
- readerThread = NativeThread.current();
+ readerThread = NativeThread.threadToSignal();
return fd;
}
}
@@ -229,7 +229,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
*/
private void endRead(boolean completed) throws SocketException {
synchronized (stateLock) {
- readerThread = 0;
+ readerThread = null;
int state = this.state;
if (state == ST_CLOSING)
tryFinishClose();
@@ -370,7 +370,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
private FileDescriptor beginWrite() throws SocketException {
synchronized (stateLock) {
ensureOpenAndConnected();
- writerThread = NativeThread.current();
+ writerThread = NativeThread.threadToSignal();
return fd;
}
}
@@ -381,7 +381,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
*/
private void endWrite(boolean completed) throws SocketException {
synchronized (stateLock) {
- writerThread = 0;
+ writerThread = null;
int state = this.state;
if (state == ST_CLOSING)
tryFinishClose();
@@ -511,7 +511,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
this.address = address;
this.port = port;
- readerThread = NativeThread.current();
+ readerThread = NativeThread.threadToSignal();
return fd;
}
}
@@ -522,7 +522,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
*/
private void endConnect(FileDescriptor fd, boolean completed) throws IOException {
synchronized (stateLock) {
- readerThread = 0;
+ readerThread = null;
int state = this.state;
if (state == ST_CLOSING)
tryFinishClose();
@@ -666,7 +666,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
ensureOpen();
if (localport == 0)
throw new SocketException("Not bound");
- readerThread = NativeThread.current();
+ readerThread = NativeThread.threadToSignal();
return fd;
}
}
@@ -678,7 +678,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
private void endAccept(boolean completed) throws SocketException {
synchronized (stateLock) {
int state = this.state;
- readerThread = 0;
+ readerThread = null;
if (state == ST_CLOSING)
tryFinishClose();
if (!completed && state >= ST_CLOSING)
@@ -844,7 +844,7 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
*/
private boolean tryClose() throws IOException {
assert Thread.holdsLock(stateLock) && state == ST_CLOSING;
- if (readerThread == 0 && writerThread == 0) {
+ if (readerThread == null && writerThread == null) {
try {
cleaner.clean();
} catch (UncheckedIOException ioe) {
@@ -1143,8 +1143,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
ensureOpenAndConnected();
if (!isInputClosed) {
Net.shutdown(fd, Net.SHUT_RD);
- if (NativeThread.isVirtualThread(readerThread)) {
- Poller.stopPoll(fdVal(fd), Net.POLLIN);
+ if (readerThread != null && readerThread.isVirtual()) {
+ Poller.stopPoll(readerThread);
}
isInputClosed = true;
}
@@ -1157,8 +1157,8 @@ public final class NioSocketImpl extends SocketImpl implements PlatformSocketImp
ensureOpenAndConnected();
if (!isOutputClosed) {
Net.shutdown(fd, Net.SHUT_WR);
- if (NativeThread.isVirtualThread(writerThread)) {
- Poller.stopPoll(fdVal(fd), Net.POLLOUT);
+ if (writerThread != null && writerThread.isVirtual()) {
+ Poller.stopPoll(writerThread);
}
isOutputClosed = true;
}
diff --git a/src/java.base/share/classes/sun/nio/ch/Poller.java b/src/java.base/share/classes/sun/nio/ch/Poller.java
index 4a2cb4d8fdf..9360bcc8327 100644
--- a/src/java.base/share/classes/sun/nio/ch/Poller.java
+++ b/src/java.base/share/classes/sun/nio/ch/Poller.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2017, 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
@@ -25,61 +25,107 @@
package sun.nio.ch;
import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.lang.ref.Reference;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.LockSupport;
import java.util.function.BooleanSupplier;
+import jdk.internal.access.JavaLangAccess;
+import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.InnocuousThread;
+import jdk.internal.misc.TerminatingThreadLocal;
+import jdk.internal.vm.Continuation;
+import jdk.internal.vm.ContinuationSupport;
import jdk.internal.vm.annotation.Stable;
/**
- * Polls file descriptors. Virtual threads invoke the poll method to park
- * until a given file descriptor is ready for I/O.
+ * I/O poller to allow virtual threads park until a file descriptor is ready for I/O.
*/
public abstract class Poller {
- private static final Pollers POLLERS;
- static {
- try {
- var pollers = new Pollers();
- pollers.start();
- POLLERS = pollers;
- } catch (IOException ioe) {
- throw new ExceptionInInitializerError(ioe);
- }
- }
+ private static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
- // the poller or sub-poller thread
+ // the poller group for the I/O pollers and poller threads
+ private static final PollerGroup POLLER_GROUP = createPollerGroup();
+
+ // the poller or sub-poller thread (used for observability only)
private @Stable Thread owner;
// maps file descriptors to parked Thread
private final Map map = new ConcurrentHashMap<>();
+ // shutdown (if supported by poller group)
+ private volatile boolean shutdown;
+
/**
* Poller mode.
*/
enum Mode {
/**
- * ReadPoller and WritePoller are dedicated platform threads that block waiting
- * for events and unpark virtual threads when file descriptors are ready for I/O.
+ * Read and write pollers are platform threads that block waiting for events and
+ * unpark virtual threads when file descriptors are ready for I/O.
*/
SYSTEM_THREADS,
/**
- * ReadPoller and WritePoller threads are virtual threads that poll for events,
- * yielding between polls and unparking virtual threads when file descriptors are
+ * Read and write pollers are virtual threads that poll for events, yielding
+ * between polls and unparking virtual threads when file descriptors are
* ready for I/O. If there are no events then the poller threads park until there
* are I/O events to poll. This mode helps to integrate polling with virtual
* thread scheduling. The approach is similar to the default scheme in "User-level
* Threading: Have Your Cake and Eat It Too" by Karsten and Barghi 2020
* (https://dl.acm.org/doi/10.1145/3379483).
*/
- VTHREAD_POLLERS
+ VTHREAD_POLLERS,
+
+ /**
+ * Read pollers are per-carrier virtual threads that poll for events, yielding
+ * between polls and unparking virtual threads when file descriptors are ready
+ * for I/O. If there are no events then the poller threads park until there
+ * are I/O events to poll. The write poller is a system-wide platform thread.
+ */
+ POLLER_PER_CARRIER
+ }
+
+ /**
+ * Create and return the PollerGroup.
+ */
+ private static PollerGroup createPollerGroup() {
+ try {
+ PollerProvider provider;
+ if (System.getProperty("jdk.pollerMode") instanceof String s) {
+ Mode mode = switch (s) {
+ case "1" -> Mode.SYSTEM_THREADS;
+ case "2" -> Mode.VTHREAD_POLLERS;
+ case "3" -> Mode.POLLER_PER_CARRIER;
+ default -> {
+ throw new RuntimeException(s + " is not a valid polling mode");
+ }
+ };
+ provider = PollerProvider.createProvider(mode);
+ } else {
+ provider = PollerProvider.createProvider();
+ }
+
+ int readPollers = pollerCount("jdk.readPollers", provider.defaultReadPollers());
+ int writePollers = pollerCount("jdk.writePollers", provider.defaultWritePollers());
+ PollerGroup group = switch (provider.pollerMode()) {
+ case SYSTEM_THREADS -> new SystemThreadsPollerGroup(provider, readPollers, writePollers);
+ case VTHREAD_POLLERS -> new VThreadsPollerGroup(provider, readPollers, writePollers);
+ case POLLER_PER_CARRIER -> new PollerPerCarrierPollerGroup(provider, writePollers);
+ };
+ group.start();
+ return group;
+ } catch (IOException ioe) {
+ throw new UncheckedIOException(ioe);
+ }
}
/**
@@ -89,9 +135,34 @@ public abstract class Poller {
}
/**
- * Returns the poller's file descriptor, used when the read and write poller threads
- * are virtual threads.
- *
+ * Closes the poller and release resources. This method can only be used to cleanup
+ * when creating a poller group fails.
+ */
+ abstract void close() throws IOException;
+
+ /**
+ * Sets the poller's thread owner.
+ */
+ private void setOwner() {
+ owner = Thread.currentThread();
+ }
+
+ /**
+ * Returns true if this poller is marked for shutdown.
+ */
+ boolean isShutdown() {
+ return shutdown;
+ }
+
+ /**
+ * Marks this poller for shutdown.
+ */
+ private void setShutdown() {
+ shutdown = true;
+ }
+
+ /**
+ * Returns the poller's file descriptor to use when polling with the master poller.
* @throws UnsupportedOperationException if not supported
*/
int fdVal() {
@@ -99,16 +170,18 @@ public abstract class Poller {
}
/**
- * Register the file descriptor. The registration is "one shot", meaning it should
- * be polled at most once.
+ * Register the file descriptor with the I/O event management facility so that it is
+ * polled when the file descriptor is ready for I/O. The registration is "one shot",
+ * meaning it should be polled at most once.
*/
- abstract void implRegister(int fdVal) throws IOException;
+ abstract void implStartPoll(int fdVal) throws IOException;
/**
- * Deregister the file descriptor.
+ * Deregister a file descriptor from the I/O event management facility. This may be
+ * a no-op in some implementations when the file descriptor has already been polled.
* @param polled true if the file descriptor has already been polled
*/
- abstract void implDeregister(int fdVal, boolean polled);
+ abstract void implStopPoll(int fdVal, boolean polled) throws IOException;
/**
* Poll for events. The {@link #polled(int)} method is invoked for each
@@ -116,15 +189,26 @@ public abstract class Poller {
*
* @param timeout if positive then block for up to {@code timeout} milliseconds,
* if zero then don't block, if -1 then block indefinitely
- * @return the number of file descriptors polled
+ * @return >0 if file descriptors are polled, 0 if no file descriptor polled
*/
abstract int poll(int timeout) throws IOException;
+ /**
+ * Wakeup the poller thread if blocked in poll so it can shutdown.
+ * @throws UnsupportedOperationException if not supported
+ */
+ void wakeupPoller() throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
/**
* Callback by the poll method when a file descriptor is polled.
*/
final void polled(int fdVal) {
- wakeup(fdVal);
+ Thread t = map.remove(fdVal);
+ if (t != null) {
+ LockSupport.unpark(t);
+ }
}
/**
@@ -132,19 +216,10 @@ public abstract class Poller {
* @param fdVal the file descriptor
* @param event POLLIN or POLLOUT
* @param nanos the waiting time or 0 to wait indefinitely
- * @param supplier supplies a boolean to indicate if the enclosing object is open
+ * @param isOpen supplies a boolean to indicate if the enclosing object is open
*/
- static void poll(int fdVal, int event, long nanos, BooleanSupplier supplier)
- throws IOException
- {
- assert nanos >= 0L;
- if (event == Net.POLLIN) {
- POLLERS.readPoller(fdVal).poll(fdVal, nanos, supplier);
- } else if (event == Net.POLLOUT) {
- POLLERS.writePoller(fdVal).poll(fdVal, nanos, supplier);
- } else {
- assert false;
- }
+ public static void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException {
+ POLLER_GROUP.poll(fdVal, event, nanos, isOpen);
}
/**
@@ -152,45 +227,24 @@ public abstract class Poller {
* @param fdVal the Selector's file descriptor
* @param nanos the waiting time or 0 to wait indefinitely
*/
- static void pollSelector(int fdVal, long nanos) throws IOException {
- assert nanos >= 0L;
- Poller poller = POLLERS.masterPoller();
- if (poller == null) {
- poller = POLLERS.readPoller(fdVal);
- }
- poller.poll(fdVal, nanos, () -> true);
+ public static void pollSelector(int fdVal, long nanos) throws IOException {
+ POLLER_GROUP.pollSelector(fdVal, nanos);
}
/**
- * If there is a thread polling the given file descriptor for the given event then
- * the thread is unparked.
+ * Unpark the given thread so that it stops polling.
*/
- static void stopPoll(int fdVal, int event) {
- if (event == Net.POLLIN) {
- POLLERS.readPoller(fdVal).wakeup(fdVal);
- } else if (event == Net.POLLOUT) {
- POLLERS.writePoller(fdVal).wakeup(fdVal);
- } else {
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * If there are any threads polling the given file descriptor then they are unparked.
- */
- static void stopPoll(int fdVal) {
- stopPoll(fdVal, Net.POLLIN);
- stopPoll(fdVal, Net.POLLOUT);
+ public static void stopPoll(Thread thread) {
+ LockSupport.unpark(thread);
}
/**
* Parks the current thread until a file descriptor is ready.
*/
- private void poll(int fdVal, long nanos, BooleanSupplier supplier) throws IOException {
- register(fdVal);
+ private void poll(int fdVal, long nanos, BooleanSupplier isOpen) throws IOException {
+ startPoll(fdVal);
try {
- boolean isOpen = supplier.getAsBoolean();
- if (isOpen) {
+ if (isOpen.getAsBoolean() && !isShutdown()) {
if (nanos > 0) {
LockSupport.parkNanos(nanos);
} else {
@@ -198,42 +252,38 @@ public abstract class Poller {
}
}
} finally {
- deregister(fdVal);
+ stopPoll(fdVal);
}
}
/**
- * Registers the file descriptor to be polled at most once when the file descriptor
- * is ready for I/O.
+ * Register a file descriptor with the I/O event management facility so that it is
+ * polled when the file descriptor is ready for I/O.
*/
- private void register(int fdVal) throws IOException {
+ private void startPoll(int fdVal) throws IOException {
Thread previous = map.put(fdVal, Thread.currentThread());
assert previous == null;
try {
- implRegister(fdVal);
+ implStartPoll(fdVal);
} catch (Throwable t) {
map.remove(fdVal);
throw t;
+ } finally {
+ Reference.reachabilityFence(this);
}
}
/**
- * Deregister the file descriptor so that the file descriptor is not polled.
+ * Deregister a file descriptor from the I/O event management facility.
*/
- private void deregister(int fdVal) {
+ private void stopPoll(int fdVal) throws IOException {
Thread previous = map.remove(fdVal);
boolean polled = (previous == null);
assert polled || previous == Thread.currentThread();
- implDeregister(fdVal, polled);
- }
-
- /**
- * Unparks any thread that is polling the given file descriptor.
- */
- private void wakeup(int fdVal) {
- Thread t = map.remove(fdVal);
- if (t != null) {
- LockSupport.unpark(t);
+ try {
+ implStopPoll(fdVal, polled);
+ } finally {
+ Reference.reachabilityFence(this);
}
}
@@ -242,9 +292,9 @@ public abstract class Poller {
* descriptor that is polled.
*/
private void pollerLoop() {
- owner = Thread.currentThread();
+ setOwner();
try {
- for (;;) {
+ while (!isShutdown()) {
poll(-1);
}
} catch (Exception e) {
@@ -263,10 +313,10 @@ public abstract class Poller {
*/
private void subPollerLoop(Poller masterPoller) {
assert Thread.currentThread().isVirtual();
- owner = Thread.currentThread();
+ setOwner();
try {
int polled = 0;
- for (;;) {
+ while (!isShutdown()) {
if (polled == 0) {
masterPoller.poll(fdVal(), 0, () -> true); // park
} else {
@@ -280,194 +330,463 @@ public abstract class Poller {
}
/**
- * Returns the number I/O operations currently registered with this poller.
+ * Unparks all threads waiting on a file descriptor registered with this poller.
*/
- public int registered() {
- return map.size();
+ private void wakeupAll() {
+ map.values().forEach(LockSupport::unpark);
}
@Override
public String toString() {
return String.format("%s [registered = %d, owner = %s]",
- Objects.toIdentityString(this), registered(), owner);
+ Objects.toIdentityString(this), map.size(), owner);
}
/**
- * The Pollers used for read and write events.
+ * A group of poller threads that support virtual threads polling file descriptors.
*/
- private static class Pollers {
+ private static abstract class PollerGroup {
private final PollerProvider provider;
- private final Poller.Mode pollerMode;
- private final Poller masterPoller;
- private final Poller[] readPollers;
- private final Poller[] writePollers;
-
- // used by start method to executor is kept alive
- private Executor executor;
-
- /**
- * Creates the Poller instances based on configuration.
- */
- Pollers() throws IOException {
- PollerProvider provider = PollerProvider.provider();
- Poller.Mode mode;
- String s = System.getProperty("jdk.pollerMode");
- if (s != null) {
- if (s.equalsIgnoreCase(Mode.SYSTEM_THREADS.name()) || s.equals("1")) {
- mode = Mode.SYSTEM_THREADS;
- } else if (s.equalsIgnoreCase(Mode.VTHREAD_POLLERS.name()) || s.equals("2")) {
- mode = Mode.VTHREAD_POLLERS;
- } else {
- throw new RuntimeException("Can't parse '" + s + "' as polling mode");
- }
- } else {
- mode = provider.defaultPollerMode();
- }
-
- // vthread poller mode needs a master poller
- Poller masterPoller = (mode == Mode.VTHREAD_POLLERS)
- ? provider.readPoller(false)
- : null;
-
- // read pollers (or sub-pollers)
- int readPollerCount = pollerCount("jdk.readPollers", provider.defaultReadPollers(mode));
- Poller[] readPollers = new Poller[readPollerCount];
- for (int i = 0; i < readPollerCount; i++) {
- readPollers[i] = provider.readPoller(mode == Mode.VTHREAD_POLLERS);
- }
-
- // write pollers (or sub-pollers)
- int writePollerCount = pollerCount("jdk.writePollers", provider.defaultWritePollers(mode));
- Poller[] writePollers = new Poller[writePollerCount];
- for (int i = 0; i < writePollerCount; i++) {
- writePollers[i] = provider.writePoller(mode == Mode.VTHREAD_POLLERS);
- }
+ PollerGroup(PollerProvider provider) {
this.provider = provider;
- this.pollerMode = mode;
- this.masterPoller = masterPoller;
- this.readPollers = readPollers;
- this.writePollers = writePollers;
+ }
+
+ final PollerProvider provider() {
+ return provider;
}
/**
- * Starts the Poller threads.
+ * Starts the poller group and any system-wide poller threads.
*/
- void start() {
- if (pollerMode == Mode.VTHREAD_POLLERS) {
- startPlatformThread("MasterPoller", masterPoller::pollerLoop);
- ThreadFactory factory = Thread.ofVirtual()
- .inheritInheritableThreadLocals(false)
- .name("SubPoller-", 0)
- .uncaughtExceptionHandler((t, e) -> e.printStackTrace())
- .factory();
- executor = Executors.newThreadPerTaskExecutor(factory);
- Arrays.stream(readPollers).forEach(p -> {
- executor.execute(() -> p.subPollerLoop(masterPoller));
- });
- Arrays.stream(writePollers).forEach(p -> {
- executor.execute(() -> p.subPollerLoop(masterPoller));
- });
- } else {
- Arrays.stream(readPollers).forEach(p -> {
- startPlatformThread("Read-Poller", p::pollerLoop);
- });
- Arrays.stream(writePollers).forEach(p -> {
- startPlatformThread("Write-Poller", p::pollerLoop);
- });
- }
- }
+ abstract void start();
/**
- * Returns the master poller, or null if there is no master poller.
+ * Parks the current thread until a file descriptor is ready for the given op.
*/
- Poller masterPoller() {
- return masterPoller;
- }
+ abstract void poll(int fdVal, int event, long nanos, BooleanSupplier isOpen) throws IOException;
/**
- * Returns the read poller for the given file descriptor.
+ * Parks the current thread until a Selector's file descriptor is ready.
*/
- Poller readPoller(int fdVal) {
- int index = provider.fdValToIndex(fdVal, readPollers.length);
- return readPollers[index];
- }
-
- /**
- * Returns the write poller for the given file descriptor.
- */
- Poller writePoller(int fdVal) {
- int index = provider.fdValToIndex(fdVal, writePollers.length);
- return writePollers[index];
- }
-
- /**
- * Return the list of read pollers.
- */
- List readPollers() {
- return List.of(readPollers);
- }
-
- /**
- * Return the list of write pollers.
- */
- List