/* * Copyright (c) 2019, 2022, 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.
*/
/* * @test id=default * @bug 8284161 8286788 * @summary Test Thread API with virtual threads * @enablePreview * @modules java.base/java.lang:+open * @library /test/lib * @run testng ThreadAPI
*/
/** * An operation that does not return a result but may throw an exception.
*/
@FunctionalInterface interface ThrowingRunnable { void run() throws Exception;
}
/** * Test Thread.currentThread before/after park.
*/
@Test publicvoid testCurrentThread1() throws Exception { var before = new AtomicReference<Thread>(); var after = new AtomicReference<Thread>(); varthread = Thread.ofVirtual().start(() -> {
before.set(Thread.currentThread());
LockSupport.park();
after.set(Thread.currentThread());
});
awaitParked(thread);
LockSupport.unpark(thread); thread.join();
assertTrue(before.get() == thread);
assertTrue(after.get() == thread);
}
/** * Test Thread.currentThread before/after entering synchronized block.
*/
@Test publicvoid testCurrentThread2() throws Exception { var ref1 = new AtomicReference<Thread>(); var ref2 = new AtomicReference<Thread>(); var ref3 = new AtomicReference<Thread>(); varthread = Thread.ofVirtual().unstarted(() -> {
ref1.set(Thread.currentThread()); synchronized (lock) {
ref2.set(Thread.currentThread());
}
ref3.set(Thread.currentThread());
}); synchronized (lock) { thread.start();
awaitBlocked(thread);
} thread.join();
assertTrue(ref1.get() == thread);
assertTrue(ref2.get() == thread);
assertTrue(ref3.get() == thread);
}
/** * Test Thread.currentThread before/after acquiring lock.
*/
@Test publicvoid testCurrentThread3() throws Exception { var ref1 = new AtomicReference<Thread>(); var ref2 = new AtomicReference<Thread>(); var ref3 = new AtomicReference<Thread>(); var lock = new ReentrantLock(); varthread = Thread.ofVirtual().unstarted(() -> {
ref1.set(Thread.currentThread());
lock.lock(); try {
ref2.set(Thread.currentThread());
} finally {
lock.unlock();
}
ref3.set(Thread.currentThread());
});
lock.lock(); try { thread.start();
awaitParked(thread);
} finally {
lock.unlock();
} thread.join();
assertTrue(ref1.get() == thread);
assertTrue(ref2.get() == thread);
assertTrue(ref3.get() == thread);
}
/** * Test Thread::run.
*/
@Test publicvoid testRun1() throws Exception { var ref = new AtomicBoolean(); varthread = Thread.ofVirtual().unstarted(() -> ref.set(true)); thread.run();
assertFalse(ref.get());
}
/** * Test Thread::start.
*/
@Test publicvoid testStart1() throws Exception { var ref = new AtomicBoolean(); varthread = Thread.ofVirtual().unstarted(() -> ref.set(true));
assertFalse(ref.get()); thread.start(); thread.join();
assertTrue(ref.get());
}
/** * Test interrupt of virtual thread blocked in Thread.join.
*/
@Test publicvoid testJoin26() throws Exception {
VThreadRunner.run(this::testJoin25);
}
/** * Test virtual thread calling Thread.join to wait for platform thread to terminate.
*/
@Test publicvoid testJoin27() throws Exception {
AtomicBoolean done = new AtomicBoolean();
VThreadRunner.run(() -> { varthread = newThread(() -> { while (!done.get()) {
LockSupport.park();
}
}); thread.start(); try {
assertFalse(thread.join(Duration.ofMillis(-100)));
assertFalse(thread.join(Duration.ofMillis(0)));
assertFalse(thread.join(Duration.ofMillis(100)));
} finally {
done.set(true);
LockSupport.unpark(thread); thread.join();
}
});
}
/** * Test virtual thread calling Thread.join to wait for platform thread to terminate.
*/
@Test publicvoid testJoin28() throws Exception { long nanos = TimeUnit.NANOSECONDS.convert(100, TimeUnit.MILLISECONDS);
VThreadRunner.run(() -> { varthread = newThread(() -> LockSupport.parkNanos(nanos)); thread.start(); try {
assertTrue(thread.join(Duration.ofSeconds(Integer.MAX_VALUE)));
assertFalse(thread.isAlive());
} finally {
LockSupport.unpark(thread); thread.join();
}
});
}
/** * Test virtual thread with interrupt status set calling Thread.join to wait * for platform thread to terminate.
*/
@Test publicvoid testJoin29() throws Exception {
VThreadRunner.run(() -> { varthread = newThread(LockSupport::park); thread.start(); Thread.currentThread().interrupt(); try { thread.join(Duration.ofSeconds(Integer.MAX_VALUE));
fail("join not interrupted");
} catch (InterruptedException expected) {
assertFalse(Thread.interrupted());
} finally {
LockSupport.unpark(thread); thread.join();
}
});
}
/** * Test interrupting virtual thread that is waiting in Thread.join for * platform thread to terminate.
*/
@Test publicvoid testJoin30() throws Exception {
VThreadRunner.run(() -> {
AtomicBoolean done = new AtomicBoolean(); varthread = newThread(() -> { while (!done.get()) {
LockSupport.park();
}
}); thread.start();
scheduleInterrupt(Thread.currentThread(), 100); try { thread.join(Duration.ofSeconds(Integer.MAX_VALUE));
fail("join not interrupted");
} catch (InterruptedException expected) {
assertFalse(Thread.interrupted());
} finally {
done.set(true);
LockSupport.unpark(thread); thread.join();
}
});
}
/** * Test platform thread invoking Thread.join on a thread that is parking * and unparking.
*/
@Test publicvoid testJoin31() throws Exception { Threadthread = Thread.ofVirtual().start(() -> { synchronized (lock) { for (int i = 0; i < 10; i++) {
LockSupport.parkNanos(Duration.ofMillis(20).toNanos());
}
}
}); thread.join();
assertFalse(thread.isAlive());
}
/** * Test virtual thread invoking Thread.join on a thread that is parking * and unparking.
*/
@Test publicvoid testJoin32() throws Exception {
VThreadRunner.run(this::testJoin31);
}
/** * Test platform thread invoking timed-Thread.join on a thread that is parking * and unparking while pinned.
*/
@Test publicvoid testJoin33() throws Exception {
AtomicBoolean done = new AtomicBoolean(); Threadthread = Thread.ofVirtual().start(() -> { synchronized (lock) { while (!done.get()) {
LockSupport.parkNanos(Duration.ofMillis(20).toNanos());
}
}
}); try {
assertFalse(thread.join(Duration.ofMillis(100)));
} finally {
done.set(true);
}
}
/** * Test virtual thread invoking timed-Thread.join on a thread that is parking * and unparking while pinned.
*/
@Test publicvoid testJoin34() throws Exception { // need at least two carrier threads due to pinning int previousParallelism = VThreadRunner.ensureParallelism(2); try {
VThreadRunner.run(this::testJoin33);
} finally { // restore
VThreadRunner.setParallelism(previousParallelism);
}
}
/** * Test trying to park with interrupt status set.
*/
@Test publicvoid testInterrupt8() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt();
LockSupport.park();
assertTrue(Thread.interrupted());
});
}
/** * Test trying to wait with interrupt status set.
*/
@Test publicvoid testInterrupt9() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt(); synchronized (lock) { try {
lock.wait();
fail("wait not interrupted");
} catch (InterruptedException expected) {
assertFalse(Thread.interrupted());
}
}
});
}
/** * Test trying to block with interrupt status set.
*/
@Test publicvoid testInterrupt10() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt(); try (Selector sel = Selector.open()) {
sel.select();
assertTrue(Thread.interrupted());
}
});
}
/** * Test Thread.getName and setName from current thread, started without name.
*/
@Test publicvoid testSetName1() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
assertTrue(me.getName().isEmpty());
me.setName("fred");
assertEquals(me.getName(), "fred");
});
}
/** * Test Thread.getName and setName from current thread, started with name.
*/
@Test publicvoid testSetName2() throws Exception {
VThreadRunner.run("fred", () -> { Thread me = Thread.currentThread();
assertEquals(me.getName(), "fred");
me.setName("joe");
assertEquals(me.getName(), "joe");
});
}
/** * Test Thread.getName and setName from another thread.
*/
@Test publicvoid testSetName3() throws Exception { varthread = Thread.ofVirtual().unstarted(LockSupport::park);
assertTrue(thread.getName().isEmpty());
// not started thread.setName("fred1");
assertEquals(thread.getName(), "fred1");
/** * Test Thread.getPriority and setPriority from another thread.
*/
@Test publicvoid testSetPriority2() throws Exception { varthread = Thread.ofVirtual().unstarted(LockSupport::park);
// not started
assertTrue(thread.getPriority() == Thread.NORM_PRIORITY);
/** * Test Thread.isDaemon and setDaemon from current thread.
*/
@Test publicvoid testSetDaemon1() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
assertTrue(me.isDaemon());
assertThrows(IllegalThreadStateException.class, () -> me.setDaemon(true));
assertThrows(IllegalArgumentException.class, () -> me.setDaemon(false));
});
}
/** * Test Thread.isDaemon and setDaemon from another thread.
*/
@Test publicvoid testSetDaemon2() throws Exception { varthread = Thread.ofVirtual().unstarted(LockSupport::park);
// not started
assertTrue(thread.isDaemon()); thread.setDaemon(true);
assertThrows(IllegalArgumentException.class, () -> thread.setDaemon(false));
/** * Tasks that sleep for zero or longer duration.
*/
@DataProvider(name = "sleepers") public Object[][] sleepers() {
ThrowingRunnable[] sleepers = {
() -> Thread.sleep(0),
() -> Thread.sleep(0, 0),
() -> Thread.sleep(1000),
() -> Thread.sleep(1000, 0),
() -> Thread.sleep(Duration.ofMillis(0)),
() -> Thread.sleep(Duration.ofMillis(1000)),
}; return Arrays.stream(sleepers)
.map(s -> new Object[] { s })
.toArray(Object[][]::new);
}
/** * Test Thread.sleep with interrupt status set.
*/
@Test(dataProvider = "sleepers") publicvoid testSleep4(ThrowingRunnable sleeper) throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt(); try {
sleeper.run();
fail("sleep was not interrupted");
} catch (InterruptedException e) { // expected
assertFalse(me.isInterrupted());
}
});
}
/** * Test Thread.sleep with interrupt status set and a negative duration.
*/
@Test publicvoid testSleep4() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt(); Thread.sleep(Duration.ofMillis(-1000)); // does nothing
assertTrue(me.isInterrupted());
});
}
/** * Tasks that sleep for a long time.
*/
@DataProvider(name = "longSleepers") public Object[][] longSleepers() {
ThrowingRunnable[] sleepers = {
() -> Thread.sleep(20_000),
() -> Thread.sleep(20_000, 0),
() -> Thread.sleep(Duration.ofSeconds(20)),
}; return Arrays.stream(sleepers)
.map(s -> new Object[] { s })
.toArray(Object[][]::new);
}
/** * Test interrupting Thread.sleep.
*/
@Test(dataProvider = "longSleepers") publicvoid testSleep5(ThrowingRunnable sleeper) throws Exception {
VThreadRunner.run(() -> { Thread t = Thread.currentThread();
scheduleInterrupt(t, 100); try {
sleeper.run();
fail("sleep was not interrupted");
} catch (InterruptedException e) { // interrupt status should be cleared
assertFalse(t.isInterrupted());
}
});
}
/** * Test that Thread.sleep does not disrupt parking permit.
*/
@Test publicvoid testSleep6() throws Exception {
VThreadRunner.run(() -> {
LockSupport.unpark(Thread.currentThread());
long start = millisTime(); Thread.sleep(1000);
expectDuration(start, /*min*/900, /*max*/4000);
// check that parking permit was not consumed
LockSupport.park();
});
}
/** * Test that Thread.sleep is not disrupted by unparking thread.
*/
@Test publicvoid testSleep7() throws Exception {
AtomicReference<Exception> exc = new AtomicReference<>(); varthread = Thread.ofVirtual().start(() -> { try { long start = millisTime(); Thread.sleep(1000);
expectDuration(start, /*min*/900, /*max*/4000);
} catch (Exception e) {
exc.set(e);
}
}); // attempt to disrupt sleep for (int i = 0; i < 5; i++) { Thread.sleep(20);
LockSupport.unpark(thread);
} thread.join();
Exception e = exc.get(); if (e != null) { throw e;
}
}
/** * Test Thread.sleep when pinned.
*/
@Test publicvoid testSleep8() throws Exception {
VThreadRunner.run(() -> { long start = millisTime(); synchronized (lock) { Thread.sleep(1000);
}
expectDuration(start, /*min*/900, /*max*/4000);
});
}
/** * Test Thread.sleep when pinned and with interrupt status set.
*/
@Test publicvoid testSleep9() throws Exception {
VThreadRunner.run(() -> { Thread me = Thread.currentThread();
me.interrupt(); try { synchronized (lock) { Thread.sleep(2000);
}
fail("sleep not interrupted");
} catch (InterruptedException e) { // expected
assertFalse(me.isInterrupted());
}
});
}
/** * Test interrupting Thread.sleep when pinned.
*/
@Test publicvoid testSleep10() throws Exception {
VThreadRunner.run(() -> { Thread t = Thread.currentThread();
scheduleInterrupt(t, 100); try { synchronized (lock) { Thread.sleep(20 * 1000);
}
fail("sleep not interrupted");
} catch (InterruptedException e) { // interrupt status should be cleared
assertFalse(t.isInterrupted());
}
});
}
/** * Returns the current time in milliseconds.
*/ privatestaticlong millisTime() { long now = System.nanoTime(); return TimeUnit.MILLISECONDS.convert(now, TimeUnit.NANOSECONDS);
}
/** * Check the duration of a task * @param start start time, in milliseconds * @param min minimum expected duration, in milliseconds * @param max maximum expected duration, in milliseconds * @return the duration (now - start), in milliseconds
*/ privatestaticvoid expectDuration(long start, long min, long max) { long duration = millisTime() - start;
assertTrue(duration >= min, "Duration " + duration + "ms, expected >= " + min + "ms");
assertTrue(duration <= max, "Duration " + duration + "ms, expected <= " + max + "ms");
}
/** * Test Thread.xxxContextClassLoader from the current thread.
*/
@Test publicvoid testContextClassLoader1() throws Exception {
ClassLoader loader = new ClassLoader() { };
VThreadRunner.run(() -> { Thread t = Thread.currentThread();
t.setContextClassLoader(loader);
assertTrue(t.getContextClassLoader() == loader);
});
}
/** * Test inheriting initial value of TCCL from platform thread.
*/
@Test publicvoid testContextClassLoader2() throws Exception {
ClassLoader loader = new ClassLoader() { }; Thread t = Thread.currentThread();
ClassLoader savedLoader = t.getContextClassLoader();
t.setContextClassLoader(loader); try {
VThreadRunner.run(() -> {
assertTrue(Thread.currentThread().getContextClassLoader() == loader);
});
} finally {
t.setContextClassLoader(savedLoader);
}
}
/** * Test inheriting initial value of TCCL from virtual thread.
*/
@Test publicvoid testContextClassLoader3() throws Exception {
VThreadRunner.run(() -> {
ClassLoader loader = new ClassLoader() { }; Thread.currentThread().setContextClassLoader(loader);
VThreadRunner.run(() -> {
assertTrue(Thread.currentThread().getContextClassLoader() == loader);
});
});
}
/** * Test inheriting initial value of TCCL through an intermediate virtual thread.
*/
@Test publicvoid testContextClassLoader4() throws Exception {
ClassLoader loader = new ClassLoader() { }; Thread t = Thread.currentThread();
ClassLoader savedLoader = t.getContextClassLoader();
t.setContextClassLoader(loader); try {
VThreadRunner.run(() -> {
VThreadRunner.run(() -> {
assertTrue(Thread.currentThread().getContextClassLoader() == loader);
});
});
} finally {
t.setContextClassLoader(savedLoader);
}
}
/** * Test Thread.xxxContextClassLoader when thread locals not supported.
*/
@Test publicvoid testContextClassLoader5() throws Exception {
ClassLoader scl = ClassLoader.getSystemClassLoader();
ClassLoader loader = new ClassLoader() { };
VThreadRunner.run(VThreadRunner.NO_THREAD_LOCALS, () -> { Thread t = Thread.currentThread();
assertTrue(t.getContextClassLoader() == scl);
assertThrows(UnsupportedOperationException.class,
() -> t.setContextClassLoader(loader));
assertTrue(t.getContextClassLoader() == scl);
});
}
/** * Test Thread.xxxContextClassLoader when thread does not inherit the * initial value of inheritable thread locals.
*/
@Test publicvoid testContextClassLoader6() throws Exception {
VThreadRunner.run(() -> {
ClassLoader loader = new ClassLoader() { }; Thread.currentThread().setContextClassLoader(loader); int characteristics = VThreadRunner.NO_INHERIT_THREAD_LOCALS;
VThreadRunner.run(characteristics, () -> { Thread t = Thread.currentThread();
assertTrue(t.getContextClassLoader() == ClassLoader.getSystemClassLoader());
t.setContextClassLoader(loader);
assertTrue(t.getContextClassLoader() == loader);
});
});
}
/** * Test Thread::threadId and getId.
*/
@Test publicvoid testThreadId1() throws Exception {
record ThreadIds(long threadId, long id) { } var ref = new AtomicReference<ThreadIds>();
/** * Test that each Thread has a unique ID
*/
@Test publicvoid testThreadId2() throws Exception { // thread ID should be unique long tid1 = Thread.ofVirtual().unstarted(() -> { }).threadId(); long tid2 = Thread.ofVirtual().unstarted(() -> { }).threadId(); long tid3 = Thread.currentThread().threadId();
assertFalse(tid1 == tid2);
assertFalse(tid1 == tid3);
assertFalse(tid2 == tid3);
}
/** * Test Thread::getState when thread is not started.
*/
@Test publicvoid testGetState1() { varthread = Thread.ofVirtual().unstarted(() -> { });
assertTrue(thread.getState() == Thread.State.NEW);
}
/** * Test Thread::getState when thread is runnable (mounted).
*/
@Test publicvoid testGetState2() throws Exception {
VThreadRunner.run(() -> { Thread.State state = Thread.currentThread().getState();
assertTrue(state == Thread.State.RUNNABLE);
});
}
/** * Test Thread::getState when thread is runnable (not mounted).
*/
@Test publicvoid testGetState3() throws Exception { if (!ThreadBuilders.supportsCustomScheduler()) thrownew SkipException("No support for custom schedulers");
AtomicBoolean completed = new AtomicBoolean(); try (ExecutorService scheduler = Executors.newFixedThreadPool(1)) { Thread.Builder builder = ThreadBuilders.virtualThreadBuilder(scheduler); Thread t1 = builder.start(() -> { Thread t2 = builder.unstarted(LockSupport::park);
assertTrue(t2.getState() == Thread.State.NEW);
// start t2 to make it runnable
t2.start(); try {
assertTrue(t2.getState() == Thread.State.RUNNABLE);
// yield to allow t2 to run and park Thread.yield();
assertTrue(t2.getState() == Thread.State.WAITING);
} finally { // unpark t2 to make it runnable again
LockSupport.unpark(t2);
}
// t2 should be runnable (not mounted)
assertTrue(t2.getState() == Thread.State.RUNNABLE);
// get carrier Thread Thread carrier; while ((carrier = ref.get()) == null) { Thread.sleep(20);
}
// wait for virtual thread to block in wait while (vthread.getState() != Thread.State.WAITING) { Thread.sleep(20);
}
// get stack trace of both carrier and virtual thread
StackTraceElement[] carrierStackTrace = carrier.getStackTrace();
StackTraceElement[] vthreadStackTrace = vthread.getStackTrace();
/** * Test Thread::getThreadGroup on virtual thread created by platform thread.
*/
@Test publicvoid testThreadGroup1() throws Exception { varthread = Thread.ofVirtual().unstarted(LockSupport::park); var vgroup = thread.getThreadGroup(); thread.start(); try {
assertTrue(thread.getThreadGroup() == vgroup);
} finally {
LockSupport.unpark(thread); thread.join();
}
assertTrue(thread.getThreadGroup() == null);
}
/** * Test Thread::getThreadGroup on platform thread created by virtual thread.
*/
@Test publicvoid testThreadGroup2() throws Exception {
VThreadRunner.run(() -> {
ThreadGroup vgroup = Thread.currentThread().getThreadGroup(); Thread child = newThread(() -> { });
ThreadGroup group = child.getThreadGroup();
assertTrue(group == vgroup);
});
}
/** * Test ThreadGroup returned by Thread::getThreadGroup and subgroup * created with 2-arg ThreadGroup constructor.
*/
@Test publicvoid testThreadGroup3() throws Exception {
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.