Skip to content

Commit 9baaff9

Browse files
committed
Test changes + JFR Updates + Library code changes
1 parent 21c3353 commit 9baaff9

File tree

70 files changed

+3585
-1273
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3585
-1273
lines changed

src/hotspot/share/include/jvm.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,6 +1151,9 @@ JVM_VirtualThreadHideFrames(JNIEnv* env, jclass clazz, jboolean hide);
11511151
JNIEXPORT void JNICALL
11521152
JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jboolean enter);
11531153

1154+
JNIEXPORT void JNICALL
1155+
JVM_VirtualThreadPinnedEvent(jint reasonCode, jstring reasonString);
1156+
11541157
JNIEXPORT jobject JNICALL
11551158
JVM_TakeVirtualThreadListToUnblock(JNIEnv* env, jclass ignored);
11561159

src/hotspot/share/jfr/metadata/metadata.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@
156156
<Field type="ulong" name="id" label="Continuation ID" />
157157
</Event>
158158

159+
<Event name="VirtualThreadPinned" category="Java Virtual Machine, Runtime" label="Virtual Thread Pinned" thread="true" stackTrace="true" startTime="false">
160+
<Field type="string" name="pinnedReason" label="Pinned Reason" />
161+
<Field type="Thread" name="carrierThread" label="Carrier Thread" />
162+
</Event>
163+
159164
<Event name="ReservedStackActivation" category="Java Virtual Machine, Runtime" label="Reserved Stack Activation"
160165
description="Activation of Reserved Stack Area caused by stack overflow with ReservedStackAccess annotated method in call stack" thread="true" stackTrace="true"
161166
startTime="false">

src/hotspot/share/oops/instanceKlass.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,22 @@ void InstanceKlass::initialize_impl(TRAPS) {
11051105
}
11061106
wait = true;
11071107
jt->set_class_to_be_initialized(this);
1108+
1109+
#if INCLUDE_JFR
1110+
ContinuationEntry* ce = jt->last_continuation();
1111+
if (ce != nullptr && ce->is_virtual_thread()) {
1112+
EventVirtualThreadPinned e;
1113+
if (e.should_commit()) {
1114+
ResourceMark rm(jt);
1115+
char reason[256];
1116+
jio_snprintf(reason, sizeof reason, "Waiting for initialization of klass %s", external_name());
1117+
e.set_pinnedReason(reason);
1118+
e.set_carrierThread(JFR_JVM_THREAD_ID(THREAD));
1119+
e.commit();
1120+
}
1121+
}
1122+
#endif
1123+
11081124
ol.wait_uninterruptibly(jt);
11091125
jt->set_class_to_be_initialized(nullptr);
11101126
}

src/hotspot/share/prims/jvm.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3979,6 +3979,20 @@ JVM_ENTRY(void, JVM_VirtualThreadDisableSuspend(JNIEnv* env, jclass clazz, jbool
39793979
#endif
39803980
JVM_END
39813981

3982+
JVM_ENTRY_NO_ENV(void, JVM_VirtualThreadPinnedEvent(jint reasonCode, jstring reasonString))
3983+
#if INCLUDE_JFR
3984+
EventVirtualThreadPinned e;
3985+
if (e.should_commit()) {
3986+
ResourceMark rm(THREAD);
3987+
// ignore reason code for now
3988+
const char *reason = java_lang_String::as_utf8_string(JNIHandles::resolve_non_null(reasonString));
3989+
e.set_pinnedReason(reason);
3990+
e.set_carrierThread(JFR_JVM_THREAD_ID(THREAD));
3991+
e.commit();
3992+
}
3993+
#endif
3994+
JVM_END
3995+
39823996
JVM_ENTRY(jobject, JVM_TakeVirtualThreadListToUnblock(JNIEnv* env, jclass ignored))
39833997
ParkEvent* parkEvent = ObjectMonitor::vthread_unparker_ParkEvent();
39843998
assert(parkEvent != nullptr, "not initialized");

src/hotspot/share/runtime/objectMonitor.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ OopStorage* ObjectMonitor::_oop_storage = nullptr;
119119
OopHandle ObjectMonitor::_vthread_cxq_head;
120120
ParkEvent* ObjectMonitor::_vthread_unparker_ParkEvent = nullptr;
121121

122+
static void post_virtual_thread_pinned_event(JavaThread* current, const char* reason) {
123+
EventVirtualThreadPinned e;
124+
if (e.should_commit()) {
125+
e.set_pinnedReason(reason);
126+
e.set_carrierThread(JFR_JVM_THREAD_ID(current));
127+
e.commit();
128+
}
129+
}
130+
122131
// -----------------------------------------------------------------------------
123132
// Theory of operations -- Monitors lists, thread residency, etc:
124133
//
@@ -519,6 +528,11 @@ void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonito
519528
(!acquired && !current->preemption_cancelled() && state == java_lang_VirtualThread::BLOCKING), "invariant");
520529
return;
521530
}
531+
if (result == freeze_pinned_native) {
532+
post_virtual_thread_pinned_event(current, "Native frame or <clinit> on stack");
533+
} else if (result == freeze_unsupported) {
534+
post_virtual_thread_pinned_event(current, "Native frame or <clinit> or monitors on stack");
535+
}
522536
}
523537

524538
OSThreadContendState osts(current->osthread());
@@ -1676,6 +1690,16 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
16761690
current->set_current_waiting_monitor(nullptr);
16771691
return;
16781692
}
1693+
if (result == freeze_pinned_native || result == freeze_unsupported) {
1694+
const Klass* monitor_klass = object()->klass();
1695+
if (!is_excluded(monitor_klass)) {
1696+
if (result == freeze_pinned_native) {
1697+
post_virtual_thread_pinned_event(current,"Native frame or <clinit> on stack");
1698+
} else if (result == freeze_unsupported) {
1699+
post_virtual_thread_pinned_event(current, "Native frame or <clinit> or monitors on stack");
1700+
}
1701+
}
1702+
}
16791703
}
16801704

16811705
// create a node to be put into the queue
@@ -1857,7 +1881,6 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
18571881
// Monitor notify has precedence over thread interrupt.
18581882
}
18591883

1860-
18611884
// Consider:
18621885
// If the lock is cool (cxq == null && succ == null) and we're on an MP system
18631886
// then instead of transferring a thread from the WaitSet to the EntryList

src/java.base/linux/classes/sun/nio/ch/EPollSelectorImpl.java

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.Map;
3636
import java.util.concurrent.TimeUnit;
3737
import java.util.function.Consumer;
38-
import jdk.internal.misc.Blocker;
3938

4039
import static sun.nio.ch.EPoll.EPOLLIN;
4140
import static sun.nio.ch.EPoll.EPOLL_CTL_ADD;
@@ -105,36 +104,71 @@ protected int doSelect(Consumer<SelectionKey> action, long timeout)
105104
int numEntries;
106105
processUpdateQueue();
107106
processDeregisterQueue();
108-
try {
109-
begin(blocking);
110107

111-
do {
112-
long startTime = timedPoll ? System.nanoTime() : 0;
113-
boolean attempted = Blocker.begin(blocking);
114-
try {
108+
if (Thread.currentThread().isVirtual()) {
109+
numEntries = (timedPoll)
110+
? timedPoll(TimeUnit.MILLISECONDS.toNanos(to))
111+
: untimedPoll(blocking);
112+
} else {
113+
try {
114+
begin(blocking);
115+
do {
116+
long startTime = timedPoll ? System.nanoTime() : 0;
115117
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, to);
116-
} finally {
117-
Blocker.end(attempted);
118-
}
119-
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
120-
// timed poll interrupted so need to adjust timeout
121-
long adjust = System.nanoTime() - startTime;
122-
to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust);
123-
if (to <= 0) {
124-
// timeout expired so no retry
125-
numEntries = 0;
118+
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
119+
// timed poll interrupted so need to adjust timeout
120+
long adjust = System.nanoTime() - startTime;
121+
to -= (int) TimeUnit.NANOSECONDS.toMillis(adjust);
122+
if (to <= 0) {
123+
// timeout expired so no retry
124+
numEntries = 0;
125+
}
126126
}
127-
}
128-
} while (numEntries == IOStatus.INTERRUPTED);
129-
assert IOStatus.check(numEntries);
130-
131-
} finally {
132-
end(blocking);
127+
} while (numEntries == IOStatus.INTERRUPTED);
128+
} finally {
129+
end(blocking);
130+
}
133131
}
132+
assert IOStatus.check(numEntries);
133+
134134
processDeregisterQueue();
135135
return processEvents(numEntries, action);
136136
}
137137

138+
/**
139+
* If blocking, parks the current virtual thread until a file descriptor is polled
140+
* or the thread is interrupted.
141+
*/
142+
private int untimedPoll(boolean block) throws IOException {
143+
int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
144+
if (block) {
145+
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
146+
Poller.pollSelector(epfd, 0);
147+
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
148+
}
149+
}
150+
return numEntries;
151+
}
152+
153+
/**
154+
* Parks the current virtual thread until a file descriptor is polled, or the thread
155+
* is interrupted, for up to the specified waiting time.
156+
*/
157+
private int timedPoll(long nanos) throws IOException {
158+
long startNanos = System.nanoTime();
159+
int numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
160+
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
161+
long remainingNanos = nanos - (System.nanoTime() - startNanos);
162+
if (remainingNanos <= 0) {
163+
// timeout
164+
break;
165+
}
166+
Poller.pollSelector(epfd, remainingNanos);
167+
numEntries = EPoll.wait(epfd, pollArrayAddress, NUM_EPOLLEVENTS, 0);
168+
}
169+
return numEntries;
170+
}
171+
138172
/**
139173
* Process changes to the interest ops.
140174
*/

src/java.base/macosx/classes/sun/nio/ch/KQueueSelectorImpl.java

Lines changed: 57 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
import java.util.Map;
3636
import java.util.concurrent.TimeUnit;
3737
import java.util.function.Consumer;
38-
import jdk.internal.misc.Blocker;
3938

4039
import static sun.nio.ch.KQueue.EVFILT_READ;
4140
import static sun.nio.ch.KQueue.EVFILT_WRITE;
@@ -109,36 +108,71 @@ protected int doSelect(Consumer<SelectionKey> action, long timeout)
109108
int numEntries;
110109
processUpdateQueue();
111110
processDeregisterQueue();
112-
try {
113-
begin(blocking);
114111

115-
do {
116-
long startTime = timedPoll ? System.nanoTime() : 0;
117-
boolean attempted = Blocker.begin(blocking);
118-
try {
112+
if (Thread.currentThread().isVirtual()) {
113+
numEntries = (timedPoll)
114+
? timedPoll(TimeUnit.MILLISECONDS.toNanos(to))
115+
: untimedPoll(blocking);
116+
} else {
117+
try {
118+
begin(blocking);
119+
do {
120+
long startTime = timedPoll ? System.nanoTime() : 0;
119121
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, to);
120-
} finally {
121-
Blocker.end(attempted);
122-
}
123-
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
124-
// timed poll interrupted so need to adjust timeout
125-
long adjust = System.nanoTime() - startTime;
126-
to -= TimeUnit.NANOSECONDS.toMillis(adjust);
127-
if (to <= 0) {
128-
// timeout expired so no retry
129-
numEntries = 0;
122+
if (numEntries == IOStatus.INTERRUPTED && timedPoll) {
123+
// timed poll interrupted so need to adjust timeout
124+
long adjust = System.nanoTime() - startTime;
125+
to -= TimeUnit.NANOSECONDS.toMillis(adjust);
126+
if (to <= 0) {
127+
// timeout expired so no retry
128+
numEntries = 0;
129+
}
130130
}
131-
}
132-
} while (numEntries == IOStatus.INTERRUPTED);
133-
assert IOStatus.check(numEntries);
134-
135-
} finally {
136-
end(blocking);
131+
} while (numEntries == IOStatus.INTERRUPTED);
132+
} finally {
133+
end(blocking);
134+
}
137135
}
136+
assert IOStatus.check(numEntries);
137+
138138
processDeregisterQueue();
139139
return processEvents(numEntries, action);
140140
}
141141

142+
/**
143+
* If blocking, parks the current virtual thread until a file descriptor is polled
144+
* or the thread is interrupted.
145+
*/
146+
private int untimedPoll(boolean block) throws IOException {
147+
int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
148+
if (block) {
149+
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
150+
Poller.pollSelector(kqfd, 0);
151+
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
152+
}
153+
}
154+
return numEntries;
155+
}
156+
157+
/**
158+
* Parks the current virtual thread until a file descriptor is polled, or the thread
159+
* is interrupted, for up to the specified waiting time.
160+
*/
161+
private int timedPoll(long nanos) throws IOException {
162+
long startNanos = System.nanoTime();
163+
int numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
164+
while (numEntries == 0 && !Thread.currentThread().isInterrupted()) {
165+
long remainingNanos = nanos - (System.nanoTime() - startNanos);
166+
if (remainingNanos <= 0) {
167+
// timeout
168+
break;
169+
}
170+
Poller.pollSelector(kqfd, remainingNanos);
171+
numEntries = KQueue.poll(kqfd, pollArrayAddress, MAX_KEVENTS, 0);
172+
}
173+
return numEntries;
174+
}
175+
142176
/**
143177
* Process changes to the interest ops.
144178
*/

src/java.base/share/classes/java/io/ByteArrayOutputStream.java

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,8 @@ public void writeBytes(byte[] b) {
159159
* @throws NullPointerException if {@code out} is {@code null}.
160160
* @throws IOException if an I/O error occurs.
161161
*/
162-
public void writeTo(OutputStream out) throws IOException {
163-
if (Thread.currentThread().isVirtual()) {
164-
byte[] bytes;
165-
synchronized (this) {
166-
bytes = Arrays.copyOf(buf, count);
167-
}
168-
out.write(bytes);
169-
} else synchronized (this) {
170-
out.write(buf, 0, count);
171-
}
162+
public synchronized void writeTo(OutputStream out) throws IOException {
163+
out.write(buf, 0, count);
172164
}
173165

174166
/**

0 commit comments

Comments
 (0)