2586f36120
Reviewed-by: lmesnik, cjplummer, psandoz, mchung, sspitsyn, jpai
806 lines
28 KiB
Java
806 lines
28 KiB
Java
/*
|
|
* Copyright (c) 2019, 2023, 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
|
|
* @summary Unit test for Thread.Builder
|
|
* @run junit BuilderTest
|
|
*/
|
|
|
|
import java.util.concurrent.*;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
import java.util.concurrent.atomic.AtomicReference;
|
|
import java.util.concurrent.locks.LockSupport;
|
|
|
|
import org.junit.jupiter.api.Test;
|
|
import static org.junit.jupiter.api.Assertions.*;
|
|
import static org.junit.jupiter.api.Assumptions.*;
|
|
|
|
class BuilderTest {
|
|
|
|
/**
|
|
* Test Thread.ofPlatform to create platform threads.
|
|
*/
|
|
@Test
|
|
void testPlatformThread() throws Exception {
|
|
Thread parent = Thread.currentThread();
|
|
Thread.Builder.OfPlatform builder = Thread.ofPlatform();
|
|
|
|
// unstarted
|
|
AtomicBoolean done1 = new AtomicBoolean();
|
|
Thread thread1 = builder.unstarted(() -> done1.set(true));
|
|
assertFalse(thread1.isVirtual());
|
|
assertTrue(thread1.getState() == Thread.State.NEW);
|
|
assertFalse(thread1.getName().isEmpty());
|
|
assertTrue(thread1.getThreadGroup() == parent.getThreadGroup());
|
|
assertTrue(thread1.isDaemon() == parent.isDaemon());
|
|
assertTrue(thread1.getPriority() == parent.getPriority());
|
|
assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader());
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done1.get());
|
|
|
|
// start
|
|
AtomicBoolean done2 = new AtomicBoolean();
|
|
Thread thread2 = builder.start(() -> done2.set(true));
|
|
assertFalse(thread2.isVirtual());
|
|
assertTrue(thread2.getState() != Thread.State.NEW);
|
|
assertFalse(thread2.getName().isEmpty());
|
|
ThreadGroup group2 = thread2.getThreadGroup();
|
|
assertTrue(group2 == parent.getThreadGroup() || group2 == null);
|
|
assertTrue(thread2.isDaemon() == parent.isDaemon());
|
|
assertTrue(thread2.getPriority() == parent.getPriority());
|
|
assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader());
|
|
thread2.join();
|
|
assertTrue(done2.get());
|
|
|
|
// factory
|
|
AtomicBoolean done3 = new AtomicBoolean();
|
|
Thread thread3 = builder.factory().newThread(() -> done3.set(true));
|
|
assertFalse(thread3.isVirtual());
|
|
assertTrue(thread3.getState() == Thread.State.NEW);
|
|
assertFalse(thread3.getName().isEmpty());
|
|
assertTrue(thread3.getThreadGroup() == parent.getThreadGroup());
|
|
assertTrue(thread3.isDaemon() == parent.isDaemon());
|
|
assertTrue(thread3.getPriority() == parent.getPriority());
|
|
assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader());
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done3.get());
|
|
}
|
|
|
|
/**
|
|
* Test Thread.ofVirtual to create virtual threads.
|
|
*/
|
|
@Test
|
|
void testVirtualThread() throws Exception {
|
|
Thread parent = Thread.currentThread();
|
|
Thread.Builder.OfVirtual builder = Thread.ofVirtual();
|
|
|
|
// unstarted
|
|
AtomicBoolean done1 = new AtomicBoolean();
|
|
Thread thread1 = builder.unstarted(() -> done1.set(true));
|
|
assertTrue(thread1.isVirtual());
|
|
assertTrue(thread1.getState() == Thread.State.NEW);
|
|
assertTrue(thread1.getName().isEmpty());
|
|
assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader());
|
|
assertTrue(thread1.isDaemon());
|
|
assertTrue(thread1.getPriority() == Thread.NORM_PRIORITY);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done1.get());
|
|
|
|
// start
|
|
AtomicBoolean done2 = new AtomicBoolean();
|
|
Thread thread2 = builder.start(() -> done2.set(true));
|
|
assertTrue(thread2.isVirtual());
|
|
assertTrue(thread2.getState() != Thread.State.NEW);
|
|
assertTrue(thread2.getName().isEmpty());
|
|
assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader());
|
|
assertTrue(thread2.isDaemon());
|
|
assertTrue(thread2.getPriority() == Thread.NORM_PRIORITY);
|
|
thread2.join();
|
|
assertTrue(done2.get());
|
|
|
|
// factory
|
|
AtomicBoolean done3 = new AtomicBoolean();
|
|
Thread thread3 = builder.factory().newThread(() -> done3.set(true));
|
|
assertTrue(thread3.isVirtual());
|
|
assertTrue(thread3.getState() == Thread.State.NEW);
|
|
assertTrue(thread3.getName().isEmpty());
|
|
assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader());
|
|
assertTrue(thread3.isDaemon());
|
|
assertTrue(thread3.getPriority() == Thread.NORM_PRIORITY);
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done3.get());
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.name.
|
|
*/
|
|
@Test
|
|
void testName1() {
|
|
Thread.Builder builder = Thread.ofPlatform().name("duke");
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.getName().equals("duke"));
|
|
assertTrue(thread2.getName().equals("duke"));
|
|
assertTrue(thread3.getName().equals("duke"));
|
|
}
|
|
|
|
@Test
|
|
void testName2() {
|
|
Thread.Builder builder = Thread.ofVirtual().name("duke");
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.getName().equals("duke"));
|
|
assertTrue(thread2.getName().equals("duke"));
|
|
assertTrue(thread3.getName().equals("duke"));
|
|
}
|
|
|
|
@Test
|
|
void testName3() {
|
|
Thread.Builder builder = Thread.ofPlatform().name("duke-", 100);
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.unstarted(() -> { });
|
|
Thread thread3 = builder.unstarted(() -> { });
|
|
|
|
assertTrue(thread1.getName().equals("duke-100"));
|
|
assertTrue(thread2.getName().equals("duke-101"));
|
|
assertTrue(thread3.getName().equals("duke-102"));
|
|
|
|
ThreadFactory factory = builder.factory();
|
|
Thread thread4 = factory.newThread(() -> { });
|
|
Thread thread5 = factory.newThread(() -> { });
|
|
Thread thread6 = factory.newThread(() -> { });
|
|
|
|
assertTrue(thread4.getName().equals("duke-103"));
|
|
assertTrue(thread5.getName().equals("duke-104"));
|
|
assertTrue(thread6.getName().equals("duke-105"));
|
|
}
|
|
|
|
@Test
|
|
void testName4() {
|
|
Thread.Builder builder = Thread.ofVirtual().name("duke-", 100);
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.unstarted(() -> { });
|
|
Thread thread3 = builder.unstarted(() -> { });
|
|
|
|
assertTrue(thread1.getName().equals("duke-100"));
|
|
assertTrue(thread2.getName().equals("duke-101"));
|
|
assertTrue(thread3.getName().equals("duke-102"));
|
|
|
|
ThreadFactory factory = builder.factory();
|
|
Thread thread4 = factory.newThread(() -> { });
|
|
Thread thread5 = factory.newThread(() -> { });
|
|
Thread thread6 = factory.newThread(() -> { });
|
|
|
|
assertTrue(thread4.getName().equals("duke-103"));
|
|
assertTrue(thread5.getName().equals("duke-104"));
|
|
assertTrue(thread6.getName().equals("duke-105"));
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.OfPlatform.group.
|
|
*/
|
|
@Test
|
|
void testThreadGroup1() {
|
|
ThreadGroup group = new ThreadGroup("groupies");
|
|
Thread.Builder builder = Thread.ofPlatform().group(group);
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
|
|
AtomicBoolean done = new AtomicBoolean();
|
|
Thread thread2 = builder.start(() -> {
|
|
while (!done.get()) {
|
|
LockSupport.park();
|
|
}
|
|
});
|
|
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
try {
|
|
assertTrue(thread1.getThreadGroup() == group);
|
|
assertTrue(thread2.getThreadGroup() == group);
|
|
assertTrue(thread3.getThreadGroup() == group);
|
|
} finally {
|
|
done.set(true);
|
|
LockSupport.unpark(thread2);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
void testThreadGroup2() {
|
|
ThreadGroup vgroup = Thread.ofVirtual().unstarted(() -> { }).getThreadGroup();
|
|
assertEquals("VirtualThreads", vgroup.getName());
|
|
|
|
Thread thread1 = Thread.ofVirtual().unstarted(() -> { });
|
|
Thread thread2 = Thread.ofVirtual().start(LockSupport::park);
|
|
Thread thread3 = Thread.ofVirtual().factory().newThread(() -> { });
|
|
|
|
try {
|
|
assertTrue(thread1.getThreadGroup() == vgroup);
|
|
assertTrue(thread2.getThreadGroup() == vgroup);
|
|
assertTrue(thread3.getThreadGroup() == vgroup);
|
|
} finally {
|
|
LockSupport.unpark(thread2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.OfPlatform.priority.
|
|
*/
|
|
@Test
|
|
void testPriority1() {
|
|
int priority = Thread.currentThread().getPriority();
|
|
|
|
Thread.Builder builder = Thread.ofPlatform();
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.getPriority() == priority);
|
|
assertTrue(thread2.getPriority() == priority);
|
|
assertTrue(thread3.getPriority() == priority);
|
|
}
|
|
|
|
@Test
|
|
void testPriority2() {
|
|
int priority = Thread.MIN_PRIORITY;
|
|
|
|
Thread.Builder builder = Thread.ofPlatform().priority(priority);
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.getPriority() == priority);
|
|
assertTrue(thread2.getPriority() == priority);
|
|
assertTrue(thread3.getPriority() == priority);
|
|
}
|
|
|
|
@Test
|
|
void testPriority3() {
|
|
Thread currentThread = Thread.currentThread();
|
|
assumeFalse(currentThread.isVirtual(), "Main thread is a virtual thread");
|
|
|
|
int maxPriority = currentThread.getThreadGroup().getMaxPriority();
|
|
int priority = Math.min(maxPriority + 1, Thread.MAX_PRIORITY);
|
|
|
|
Thread.Builder builder = Thread.ofPlatform().priority(priority);
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.getPriority() == priority);
|
|
assertTrue(thread2.getPriority() == priority);
|
|
assertTrue(thread3.getPriority() == priority);
|
|
}
|
|
|
|
@Test
|
|
void testPriority4() {
|
|
var builder = Thread.ofPlatform();
|
|
assertThrows(IllegalArgumentException.class,
|
|
() -> builder.priority(Thread.MIN_PRIORITY - 1));
|
|
}
|
|
|
|
@Test
|
|
void testPriority5() {
|
|
var builder = Thread.ofPlatform();
|
|
assertThrows(IllegalArgumentException.class,
|
|
() -> builder.priority(Thread.MAX_PRIORITY + 1));
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.OfPlatform.daemon.
|
|
*/
|
|
@Test
|
|
void testDaemon1() {
|
|
Thread.Builder builder = Thread.ofPlatform().daemon(false);
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertFalse(thread1.isDaemon());
|
|
assertFalse(thread2.isDaemon());
|
|
assertFalse(thread3.isDaemon());
|
|
}
|
|
|
|
@Test
|
|
void testDaemon2() {
|
|
Thread.Builder builder = Thread.ofPlatform().daemon(true);
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.isDaemon());
|
|
assertTrue(thread2.isDaemon());
|
|
assertTrue(thread3.isDaemon());
|
|
}
|
|
|
|
@Test
|
|
void testDaemon3() {
|
|
Thread.Builder builder = Thread.ofPlatform().daemon();
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
assertTrue(thread1.isDaemon());
|
|
assertTrue(thread2.isDaemon());
|
|
assertTrue(thread3.isDaemon());
|
|
}
|
|
|
|
@Test
|
|
void testDaemon4() {
|
|
Thread.Builder builder = Thread.ofPlatform();
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
// daemon status should be inherited
|
|
boolean d = Thread.currentThread().isDaemon();
|
|
assertTrue(thread1.isDaemon() == d);
|
|
assertTrue(thread2.isDaemon() == d);
|
|
assertTrue(thread3.isDaemon() == d);
|
|
}
|
|
|
|
/**
|
|
* Test Thread.ofVirtual creates daemon threads.
|
|
*/
|
|
@Test
|
|
void testDaemon5() {
|
|
Thread.Builder builder = Thread.ofVirtual();
|
|
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
|
|
// daemon status should always be true
|
|
assertTrue(thread1.isDaemon());
|
|
assertTrue(thread2.isDaemon());
|
|
assertTrue(thread3.isDaemon());
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.OfPlatform.stackSize.
|
|
*/
|
|
@Test
|
|
void testStackSize1() {
|
|
Thread.Builder builder = Thread.ofPlatform().stackSize(1024*1024);
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
}
|
|
|
|
@Test
|
|
void testStackSize2() {
|
|
Thread.Builder builder = Thread.ofPlatform().stackSize(0);
|
|
Thread thread1 = builder.unstarted(() -> { });
|
|
Thread thread2 = builder.start(() -> { });
|
|
Thread thread3 = builder.factory().newThread(() -> { });
|
|
}
|
|
|
|
@Test
|
|
void testStackSize3() {
|
|
var builder = Thread.ofPlatform();
|
|
assertThrows(IllegalArgumentException.class, () -> builder.stackSize(-1));
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder.uncaughtExceptionHandler.
|
|
*/
|
|
@Test
|
|
void testUncaughtExceptionHandler1() throws Exception {
|
|
class FooException extends RuntimeException { }
|
|
AtomicReference<Thread> threadRef = new AtomicReference<>();
|
|
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
|
|
Thread thread = Thread.ofPlatform()
|
|
.uncaughtExceptionHandler((t, e) -> {
|
|
assertTrue(t == Thread.currentThread());
|
|
threadRef.set(t);
|
|
exceptionRef.set(e);
|
|
})
|
|
.start(() -> { throw new FooException(); });
|
|
thread.join();
|
|
assertTrue(threadRef.get() == thread);
|
|
assertTrue(exceptionRef.get() instanceof FooException);
|
|
}
|
|
|
|
@Test
|
|
void testUncaughtExceptionHandler2() throws Exception {
|
|
class FooException extends RuntimeException { }
|
|
AtomicReference<Thread> threadRef = new AtomicReference<>();
|
|
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
|
|
Thread thread = Thread.ofVirtual()
|
|
.uncaughtExceptionHandler((t, e) -> {
|
|
assertTrue(t == Thread.currentThread());
|
|
threadRef.set(t);
|
|
exceptionRef.set(e);
|
|
})
|
|
.start(() -> { throw new FooException(); });
|
|
thread.join();
|
|
assertTrue(threadRef.get() == thread);
|
|
assertTrue(exceptionRef.get() instanceof FooException);
|
|
}
|
|
|
|
@Test
|
|
void testUncaughtExceptionHandler3() throws Exception {
|
|
class FooException extends RuntimeException { }
|
|
AtomicReference<Thread> threadRef = new AtomicReference<>();
|
|
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
|
|
Thread thread = Thread.ofPlatform()
|
|
.uncaughtExceptionHandler((t, e) -> {
|
|
assertTrue(t == Thread.currentThread());
|
|
threadRef.set(t);
|
|
exceptionRef.set(e);
|
|
})
|
|
.factory()
|
|
.newThread(() -> { throw new FooException(); });
|
|
thread.start();
|
|
thread.join();
|
|
assertTrue(threadRef.get() == thread);
|
|
assertTrue(exceptionRef.get() instanceof FooException);
|
|
}
|
|
|
|
@Test
|
|
void testUncaughtExceptionHandler4() throws Exception {
|
|
class FooException extends RuntimeException { }
|
|
AtomicReference<Thread> threadRef = new AtomicReference<>();
|
|
AtomicReference<Throwable> exceptionRef = new AtomicReference<>();
|
|
Thread thread = Thread.ofPlatform()
|
|
.uncaughtExceptionHandler((t, e) -> {
|
|
assertTrue(t == Thread.currentThread());
|
|
threadRef.set(t);
|
|
exceptionRef.set(e);
|
|
})
|
|
.factory()
|
|
.newThread(() -> { throw new FooException(); });
|
|
thread.start();
|
|
thread.join();
|
|
assertTrue(threadRef.get() == thread);
|
|
assertTrue(exceptionRef.get() instanceof FooException);
|
|
}
|
|
|
|
static final ThreadLocal<Object> LOCAL = new ThreadLocal<>();
|
|
static final ThreadLocal<Object> INHERITED_LOCAL = new InheritableThreadLocal<>();
|
|
|
|
/**
|
|
* Tests that a builder creates threads that support thread locals
|
|
*/
|
|
private void testThreadLocals(Thread.Builder builder) throws Exception {
|
|
AtomicBoolean done = new AtomicBoolean();
|
|
Runnable task = () -> {
|
|
Object value = new Object();
|
|
LOCAL.set(value);
|
|
assertTrue(LOCAL.get() == value);
|
|
done.set(true);
|
|
};
|
|
|
|
done.set(false);
|
|
Thread thread1 = builder.unstarted(task);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread2 = builder.start(task);
|
|
thread2.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread3 = builder.factory().newThread(task);
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done.get());
|
|
}
|
|
|
|
/**
|
|
* Tests that a builder creates threads that do not support thread locals
|
|
*/
|
|
private void testNoThreadLocals(Thread.Builder builder) throws Exception {
|
|
AtomicBoolean done = new AtomicBoolean();
|
|
Runnable task = () -> {
|
|
try {
|
|
LOCAL.set(new Object());
|
|
} catch (UnsupportedOperationException expected) {
|
|
done.set(true);
|
|
}
|
|
};
|
|
|
|
done.set(false);
|
|
Thread thread1 = builder.unstarted(task);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread2 = builder.start(task);
|
|
thread2.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread3 = builder.factory().newThread(task);
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done.get());
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder creates threads that allow thread locals.
|
|
*/
|
|
@Test
|
|
void testThreadLocals1() throws Exception {
|
|
Thread.Builder builder = Thread.ofPlatform();
|
|
testThreadLocals(builder);
|
|
}
|
|
|
|
@Test
|
|
void testThreadLocals2() throws Exception {
|
|
Thread.Builder builder = Thread.ofVirtual();
|
|
testThreadLocals(builder);
|
|
}
|
|
|
|
/**
|
|
* Tests that a builder creates threads that inherits the initial values of
|
|
* inheritable thread locals.
|
|
*/
|
|
private void testInheritedThreadLocals(Thread.Builder builder) throws Exception {
|
|
Object value = new Object();
|
|
INHERITED_LOCAL.set(value);
|
|
|
|
AtomicBoolean done = new AtomicBoolean();
|
|
Runnable task = () -> {
|
|
assertTrue(INHERITED_LOCAL.get() == value);
|
|
done.set(true);
|
|
};
|
|
|
|
done.set(false);
|
|
Thread thread1 = builder.unstarted(task);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread2 = builder.start(task);
|
|
thread2.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread3 = builder.factory().newThread(task);
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done.get());
|
|
}
|
|
|
|
/**
|
|
* Tests that a builder creates threads that do not inherit the initial values
|
|
* of inheritable thread locals.
|
|
*/
|
|
private void testNoInheritedThreadLocals(Thread.Builder builder) throws Exception {
|
|
Object value = new Object();
|
|
INHERITED_LOCAL.set(value);
|
|
|
|
AtomicBoolean done = new AtomicBoolean();
|
|
Runnable task = () -> {
|
|
assertNull(INHERITED_LOCAL.get());
|
|
done.set(true);
|
|
};
|
|
|
|
done.set(false);
|
|
Thread thread1 = builder.unstarted(task);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread2 = builder.start(task);
|
|
thread2.join();
|
|
assertTrue(done.get());
|
|
|
|
done.set(false);
|
|
Thread thread3 = builder.factory().newThread(task);
|
|
thread3.start();
|
|
thread3.join();
|
|
assertTrue(done.get());
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder creating threads that inherit or do not inherit
|
|
* the initial values of inheritable thread locals.
|
|
*/
|
|
@Test
|
|
void testInheritedThreadLocals1() throws Exception {
|
|
Thread.Builder builder = Thread.ofPlatform();
|
|
testInheritedThreadLocals(builder); // default
|
|
|
|
// do no inherit
|
|
builder.inheritInheritableThreadLocals(false);
|
|
testNoInheritedThreadLocals(builder);
|
|
|
|
// inherit
|
|
builder.inheritInheritableThreadLocals(true);
|
|
testInheritedThreadLocals(builder);
|
|
}
|
|
|
|
@Test
|
|
void testInheritedThreadLocals2() throws Exception {
|
|
Thread.Builder builder = Thread.ofVirtual();
|
|
testInheritedThreadLocals(builder); // default
|
|
|
|
// do no inherit
|
|
builder.inheritInheritableThreadLocals(false);
|
|
testNoInheritedThreadLocals(builder);
|
|
|
|
// inherit
|
|
builder.inheritInheritableThreadLocals(true);
|
|
testInheritedThreadLocals(builder);
|
|
}
|
|
|
|
/**
|
|
* Tests a builder creates threads that inherit the context class loader.
|
|
*/
|
|
private void testInheritContextClassLoader(Thread.Builder builder) throws Exception {
|
|
ClassLoader savedCCL = Thread.currentThread().getContextClassLoader();
|
|
try {
|
|
ClassLoader loader = new ClassLoader() { };
|
|
Thread.currentThread().setContextClassLoader(loader);
|
|
|
|
var ref = new AtomicReference<ClassLoader>();
|
|
Runnable task = () -> {
|
|
ref.set(Thread.currentThread().getContextClassLoader());
|
|
};
|
|
|
|
// unstarted
|
|
Thread thread1 = builder.unstarted(task);
|
|
assertTrue(thread1.getContextClassLoader() == loader);
|
|
|
|
// started
|
|
ref.set(null);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(ref.get() == loader);
|
|
|
|
// factory
|
|
Thread thread2 = builder.factory().newThread(task);
|
|
assertTrue(thread2.getContextClassLoader() == loader);
|
|
|
|
// started
|
|
ref.set(null);
|
|
thread2.start();
|
|
thread2.join();
|
|
assertTrue(ref.get() == loader);
|
|
|
|
} finally {
|
|
Thread.currentThread().setContextClassLoader(savedCCL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests a builder creates threads does not inherit the context class loader.
|
|
*/
|
|
private void testNoInheritContextClassLoader(Thread.Builder builder) throws Exception {
|
|
ClassLoader savedCCL = Thread.currentThread().getContextClassLoader();
|
|
try {
|
|
Thread.currentThread().setContextClassLoader( new ClassLoader() { });
|
|
|
|
ClassLoader scl = ClassLoader.getSystemClassLoader();
|
|
|
|
var ref = new AtomicReference<ClassLoader>();
|
|
Runnable task = () -> {
|
|
ref.set(Thread.currentThread().getContextClassLoader());
|
|
};
|
|
|
|
// unstarted
|
|
Thread thread1 = builder.unstarted(task);
|
|
assertTrue(thread1.getContextClassLoader() == scl);
|
|
|
|
// started
|
|
ref.set(null);
|
|
thread1.start();
|
|
thread1.join();
|
|
assertTrue(ref.get() == scl);
|
|
|
|
// factory
|
|
Thread thread2 = builder.factory().newThread(task);
|
|
assertTrue(thread2.getContextClassLoader() == scl);
|
|
|
|
// started
|
|
ref.set(null);
|
|
thread2.start();
|
|
thread2.join();
|
|
assertTrue(ref.get() == scl);
|
|
|
|
} finally {
|
|
Thread.currentThread().setContextClassLoader(savedCCL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Test Thread.Builder creating threads that inherit or do not inherit
|
|
* the thread context class loader.
|
|
*/
|
|
@Test
|
|
void testContextClassLoader1() throws Exception {
|
|
Thread.Builder builder = Thread.ofPlatform();
|
|
testInheritContextClassLoader(builder); // default
|
|
|
|
// do no inherit
|
|
builder.inheritInheritableThreadLocals(false);
|
|
testNoInheritContextClassLoader(builder);
|
|
|
|
// inherit
|
|
builder.inheritInheritableThreadLocals(true);
|
|
testInheritContextClassLoader(builder);
|
|
}
|
|
|
|
@Test
|
|
void testContextClassLoader2() throws Exception {
|
|
Thread.Builder builder = Thread.ofVirtual();
|
|
testInheritContextClassLoader(builder); // default
|
|
|
|
// do no inherit
|
|
builder.inheritInheritableThreadLocals(false);
|
|
testNoInheritContextClassLoader(builder);
|
|
|
|
// inherit
|
|
builder.inheritInheritableThreadLocals(true);
|
|
testInheritContextClassLoader(builder);
|
|
}
|
|
|
|
/**
|
|
* Test NullPointerException.
|
|
*/
|
|
@Test
|
|
void testNulls1() {
|
|
Thread.Builder.OfPlatform builder = Thread.ofPlatform();
|
|
assertThrows(NullPointerException.class, () -> builder.group(null));
|
|
assertThrows(NullPointerException.class, () -> builder.name(null));
|
|
assertThrows(NullPointerException.class, () -> builder.name(null, 0));
|
|
assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null));
|
|
assertThrows(NullPointerException.class, () -> builder.unstarted(null));
|
|
assertThrows(NullPointerException.class, () -> builder.start(null));
|
|
}
|
|
|
|
@Test
|
|
void testNulls2() {
|
|
Thread.Builder builder = Thread.ofVirtual();
|
|
assertThrows(NullPointerException.class, () -> builder.name(null));
|
|
assertThrows(NullPointerException.class, () -> builder.name(null, 0));
|
|
assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null));
|
|
assertThrows(NullPointerException.class, () -> builder.unstarted(null));
|
|
assertThrows(NullPointerException.class, () -> builder.start(null));
|
|
}
|
|
}
|