1 /* 2 * Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @summary Unit test for Thread.Builder 27 * @run junit BuilderTest 28 */ 29 30 import java.util.concurrent.*; 31 import java.util.concurrent.atomic.AtomicBoolean; 32 import java.util.concurrent.atomic.AtomicReference; 33 import java.util.concurrent.locks.LockSupport; 34 35 import org.junit.jupiter.api.Test; 36 import static org.junit.jupiter.api.Assertions.*; 37 import static org.junit.jupiter.api.Assumptions.*; 38 39 class BuilderTest { 40 41 /** 42 * Test Thread.ofPlatform to create platform threads. 43 */ 44 @Test 45 void testPlatformThread() throws Exception { 46 Thread parent = Thread.currentThread(); 47 Thread.Builder.OfPlatform builder = Thread.ofPlatform(); 48 49 // unstarted 50 AtomicBoolean done1 = new AtomicBoolean(); 51 Thread thread1 = builder.unstarted(() -> done1.set(true)); 52 assertFalse(thread1.isVirtual()); 53 assertTrue(thread1.getState() == Thread.State.NEW); 54 assertFalse(thread1.getName().isEmpty()); 55 assertTrue(thread1.getThreadGroup() == parent.getThreadGroup()); 56 assertTrue(thread1.isDaemon() == parent.isDaemon()); 57 assertTrue(thread1.getPriority() == parent.getPriority()); 58 assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader()); 59 thread1.start(); 60 thread1.join(); 61 assertTrue(done1.get()); 62 63 // start 64 AtomicBoolean done2 = new AtomicBoolean(); 65 Thread thread2 = builder.start(() -> done2.set(true)); 66 assertFalse(thread2.isVirtual()); 67 assertTrue(thread2.getState() != Thread.State.NEW); 68 assertFalse(thread2.getName().isEmpty()); 69 ThreadGroup group2 = thread2.getThreadGroup(); 70 assertTrue(group2 == parent.getThreadGroup() || group2 == null); 71 assertTrue(thread2.isDaemon() == parent.isDaemon()); 72 assertTrue(thread2.getPriority() == parent.getPriority()); 73 assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader()); 74 thread2.join(); 75 assertTrue(done2.get()); 76 77 // factory 78 AtomicBoolean done3 = new AtomicBoolean(); 79 Thread thread3 = builder.factory().newThread(() -> done3.set(true)); 80 assertFalse(thread3.isVirtual()); 81 assertTrue(thread3.getState() == Thread.State.NEW); 82 assertFalse(thread3.getName().isEmpty()); 83 assertTrue(thread3.getThreadGroup() == parent.getThreadGroup()); 84 assertTrue(thread3.isDaemon() == parent.isDaemon()); 85 assertTrue(thread3.getPriority() == parent.getPriority()); 86 assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader()); 87 thread3.start(); 88 thread3.join(); 89 assertTrue(done3.get()); 90 } 91 92 /** 93 * Test Thread.ofVirtual to create virtual threads. 94 */ 95 @Test 96 void testVirtualThread() throws Exception { 97 Thread parent = Thread.currentThread(); 98 Thread.Builder.OfVirtual builder = Thread.ofVirtual(); 99 100 // unstarted 101 AtomicBoolean done1 = new AtomicBoolean(); 102 Thread thread1 = builder.unstarted(() -> done1.set(true)); 103 assertTrue(thread1.isVirtual()); 104 assertTrue(thread1.getState() == Thread.State.NEW); 105 assertTrue(thread1.getName().isEmpty()); 106 assertTrue(thread1.getContextClassLoader() == parent.getContextClassLoader()); 107 assertTrue(thread1.isDaemon()); 108 assertTrue(thread1.getPriority() == Thread.NORM_PRIORITY); 109 thread1.start(); 110 thread1.join(); 111 assertTrue(done1.get()); 112 113 // start 114 AtomicBoolean done2 = new AtomicBoolean(); 115 Thread thread2 = builder.start(() -> done2.set(true)); 116 assertTrue(thread2.isVirtual()); 117 assertTrue(thread2.getState() != Thread.State.NEW); 118 assertTrue(thread2.getName().isEmpty()); 119 assertTrue(thread2.getContextClassLoader() == parent.getContextClassLoader()); 120 assertTrue(thread2.isDaemon()); 121 assertTrue(thread2.getPriority() == Thread.NORM_PRIORITY); 122 thread2.join(); 123 assertTrue(done2.get()); 124 125 // factory 126 AtomicBoolean done3 = new AtomicBoolean(); 127 Thread thread3 = builder.factory().newThread(() -> done3.set(true)); 128 assertTrue(thread3.isVirtual()); 129 assertTrue(thread3.getState() == Thread.State.NEW); 130 assertTrue(thread3.getName().isEmpty()); 131 assertTrue(thread3.getContextClassLoader() == parent.getContextClassLoader()); 132 assertTrue(thread3.isDaemon()); 133 assertTrue(thread3.getPriority() == Thread.NORM_PRIORITY); 134 thread3.start(); 135 thread3.join(); 136 assertTrue(done3.get()); 137 } 138 139 /** 140 * Test Thread.Builder.name. 141 */ 142 @Test 143 void testName1() { 144 Thread.Builder builder = Thread.ofPlatform().name("duke"); 145 146 Thread thread1 = builder.unstarted(() -> { }); 147 Thread thread2 = builder.start(() -> { }); 148 Thread thread3 = builder.factory().newThread(() -> { }); 149 150 assertTrue(thread1.getName().equals("duke")); 151 assertTrue(thread2.getName().equals("duke")); 152 assertTrue(thread3.getName().equals("duke")); 153 } 154 155 @Test 156 void testName2() { 157 Thread.Builder builder = Thread.ofVirtual().name("duke"); 158 159 Thread thread1 = builder.unstarted(() -> { }); 160 Thread thread2 = builder.start(() -> { }); 161 Thread thread3 = builder.factory().newThread(() -> { }); 162 163 assertTrue(thread1.getName().equals("duke")); 164 assertTrue(thread2.getName().equals("duke")); 165 assertTrue(thread3.getName().equals("duke")); 166 } 167 168 @Test 169 void testName3() { 170 Thread.Builder builder = Thread.ofPlatform().name("duke-", 100); 171 172 Thread thread1 = builder.unstarted(() -> { }); 173 Thread thread2 = builder.unstarted(() -> { }); 174 Thread thread3 = builder.unstarted(() -> { }); 175 176 assertTrue(thread1.getName().equals("duke-100")); 177 assertTrue(thread2.getName().equals("duke-101")); 178 assertTrue(thread3.getName().equals("duke-102")); 179 180 ThreadFactory factory = builder.factory(); 181 Thread thread4 = factory.newThread(() -> { }); 182 Thread thread5 = factory.newThread(() -> { }); 183 Thread thread6 = factory.newThread(() -> { }); 184 185 assertTrue(thread4.getName().equals("duke-103")); 186 assertTrue(thread5.getName().equals("duke-104")); 187 assertTrue(thread6.getName().equals("duke-105")); 188 } 189 190 @Test 191 void testName4() { 192 Thread.Builder builder = Thread.ofVirtual().name("duke-", 100); 193 194 Thread thread1 = builder.unstarted(() -> { }); 195 Thread thread2 = builder.unstarted(() -> { }); 196 Thread thread3 = builder.unstarted(() -> { }); 197 198 assertTrue(thread1.getName().equals("duke-100")); 199 assertTrue(thread2.getName().equals("duke-101")); 200 assertTrue(thread3.getName().equals("duke-102")); 201 202 ThreadFactory factory = builder.factory(); 203 Thread thread4 = factory.newThread(() -> { }); 204 Thread thread5 = factory.newThread(() -> { }); 205 Thread thread6 = factory.newThread(() -> { }); 206 207 assertTrue(thread4.getName().equals("duke-103")); 208 assertTrue(thread5.getName().equals("duke-104")); 209 assertTrue(thread6.getName().equals("duke-105")); 210 } 211 212 /** 213 * Test Thread.Builder.OfPlatform.group. 214 */ 215 @Test 216 void testThreadGroup1() { 217 ThreadGroup group = new ThreadGroup("groupies"); 218 Thread.Builder builder = Thread.ofPlatform().group(group); 219 220 Thread thread1 = builder.unstarted(() -> { }); 221 222 AtomicBoolean done = new AtomicBoolean(); 223 Thread thread2 = builder.start(() -> { 224 while (!done.get()) { 225 LockSupport.park(); 226 } 227 }); 228 229 Thread thread3 = builder.factory().newThread(() -> { }); 230 231 try { 232 assertTrue(thread1.getThreadGroup() == group); 233 assertTrue(thread2.getThreadGroup() == group); 234 assertTrue(thread3.getThreadGroup() == group); 235 } finally { 236 done.set(true); 237 LockSupport.unpark(thread2); 238 } 239 } 240 241 @Test 242 void testThreadGroup2() { 243 ThreadGroup vgroup = Thread.ofVirtual().unstarted(() -> { }).getThreadGroup(); 244 assertEquals("VirtualThreads", vgroup.getName()); 245 246 Thread thread1 = Thread.ofVirtual().unstarted(() -> { }); 247 Thread thread2 = Thread.ofVirtual().start(LockSupport::park); 248 Thread thread3 = Thread.ofVirtual().factory().newThread(() -> { }); 249 250 try { 251 assertTrue(thread1.getThreadGroup() == vgroup); 252 assertTrue(thread2.getThreadGroup() == vgroup); 253 assertTrue(thread3.getThreadGroup() == vgroup); 254 } finally { 255 LockSupport.unpark(thread2); 256 } 257 } 258 259 /** 260 * Test Thread.Builder.OfPlatform.priority. 261 */ 262 @Test 263 void testPriority1() { 264 int priority = Thread.currentThread().getPriority(); 265 266 Thread.Builder builder = Thread.ofPlatform(); 267 Thread thread1 = builder.unstarted(() -> { }); 268 Thread thread2 = builder.start(() -> { }); 269 Thread thread3 = builder.factory().newThread(() -> { }); 270 271 assertTrue(thread1.getPriority() == priority); 272 assertTrue(thread2.getPriority() == priority); 273 assertTrue(thread3.getPriority() == priority); 274 } 275 276 @Test 277 void testPriority2() { 278 int priority = Thread.MIN_PRIORITY; 279 280 Thread.Builder builder = Thread.ofPlatform().priority(priority); 281 Thread thread1 = builder.unstarted(() -> { }); 282 Thread thread2 = builder.start(() -> { }); 283 Thread thread3 = builder.factory().newThread(() -> { }); 284 285 assertTrue(thread1.getPriority() == priority); 286 assertTrue(thread2.getPriority() == priority); 287 assertTrue(thread3.getPriority() == priority); 288 } 289 290 @Test 291 void testPriority3() { 292 Thread currentThread = Thread.currentThread(); 293 assumeFalse(currentThread.isVirtual(), "Main thread is a virtual thread"); 294 295 int maxPriority = currentThread.getThreadGroup().getMaxPriority(); 296 int priority = Math.min(maxPriority + 1, Thread.MAX_PRIORITY); 297 298 Thread.Builder builder = Thread.ofPlatform().priority(priority); 299 Thread thread1 = builder.unstarted(() -> { }); 300 Thread thread2 = builder.start(() -> { }); 301 Thread thread3 = builder.factory().newThread(() -> { }); 302 303 assertTrue(thread1.getPriority() == priority); 304 assertTrue(thread2.getPriority() == priority); 305 assertTrue(thread3.getPriority() == priority); 306 } 307 308 @Test 309 void testPriority4() { 310 var builder = Thread.ofPlatform(); 311 assertThrows(IllegalArgumentException.class, 312 () -> builder.priority(Thread.MIN_PRIORITY - 1)); 313 } 314 315 @Test 316 void testPriority5() { 317 var builder = Thread.ofPlatform(); 318 assertThrows(IllegalArgumentException.class, 319 () -> builder.priority(Thread.MAX_PRIORITY + 1)); 320 } 321 322 /** 323 * Test Thread.Builder.OfPlatform.daemon. 324 */ 325 @Test 326 void testDaemon1() { 327 Thread.Builder builder = Thread.ofPlatform().daemon(false); 328 329 Thread thread1 = builder.unstarted(() -> { }); 330 Thread thread2 = builder.start(() -> { }); 331 Thread thread3 = builder.factory().newThread(() -> { }); 332 333 assertFalse(thread1.isDaemon()); 334 assertFalse(thread2.isDaemon()); 335 assertFalse(thread3.isDaemon()); 336 } 337 338 @Test 339 void testDaemon2() { 340 Thread.Builder builder = Thread.ofPlatform().daemon(true); 341 342 Thread thread1 = builder.unstarted(() -> { }); 343 Thread thread2 = builder.start(() -> { }); 344 Thread thread3 = builder.factory().newThread(() -> { }); 345 346 assertTrue(thread1.isDaemon()); 347 assertTrue(thread2.isDaemon()); 348 assertTrue(thread3.isDaemon()); 349 } 350 351 @Test 352 void testDaemon3() { 353 Thread.Builder builder = Thread.ofPlatform().daemon(); 354 355 Thread thread1 = builder.unstarted(() -> { }); 356 Thread thread2 = builder.start(() -> { }); 357 Thread thread3 = builder.factory().newThread(() -> { }); 358 359 assertTrue(thread1.isDaemon()); 360 assertTrue(thread2.isDaemon()); 361 assertTrue(thread3.isDaemon()); 362 } 363 364 @Test 365 void testDaemon4() { 366 Thread.Builder builder = Thread.ofPlatform(); 367 368 Thread thread1 = builder.unstarted(() -> { }); 369 Thread thread2 = builder.start(() -> { }); 370 Thread thread3 = builder.factory().newThread(() -> { }); 371 372 // daemon status should be inherited 373 boolean d = Thread.currentThread().isDaemon(); 374 assertTrue(thread1.isDaemon() == d); 375 assertTrue(thread2.isDaemon() == d); 376 assertTrue(thread3.isDaemon() == d); 377 } 378 379 /** 380 * Test Thread.ofVirtual creates daemon threads. 381 */ 382 @Test 383 void testDaemon5() { 384 Thread.Builder builder = Thread.ofVirtual(); 385 386 Thread thread1 = builder.unstarted(() -> { }); 387 Thread thread2 = builder.start(() -> { }); 388 Thread thread3 = builder.factory().newThread(() -> { }); 389 390 // daemon status should always be true 391 assertTrue(thread1.isDaemon()); 392 assertTrue(thread2.isDaemon()); 393 assertTrue(thread3.isDaemon()); 394 } 395 396 /** 397 * Test Thread.Builder.OfPlatform.stackSize. 398 */ 399 @Test 400 void testStackSize1() { 401 Thread.Builder builder = Thread.ofPlatform().stackSize(1024*1024); 402 Thread thread1 = builder.unstarted(() -> { }); 403 Thread thread2 = builder.start(() -> { }); 404 Thread thread3 = builder.factory().newThread(() -> { }); 405 } 406 407 @Test 408 void testStackSize2() { 409 Thread.Builder builder = Thread.ofPlatform().stackSize(0); 410 Thread thread1 = builder.unstarted(() -> { }); 411 Thread thread2 = builder.start(() -> { }); 412 Thread thread3 = builder.factory().newThread(() -> { }); 413 } 414 415 @Test 416 void testStackSize3() { 417 var builder = Thread.ofPlatform(); 418 assertThrows(IllegalArgumentException.class, () -> builder.stackSize(-1)); 419 } 420 421 /** 422 * Test Thread.Builder.uncaughtExceptionHandler. 423 */ 424 @Test 425 void testUncaughtExceptionHandler1() throws Exception { 426 class FooException extends RuntimeException { } 427 AtomicReference<Thread> threadRef = new AtomicReference<>(); 428 AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); 429 Thread thread = Thread.ofPlatform() 430 .uncaughtExceptionHandler((t, e) -> { 431 assertTrue(t == Thread.currentThread()); 432 threadRef.set(t); 433 exceptionRef.set(e); 434 }) 435 .start(() -> { throw new FooException(); }); 436 thread.join(); 437 assertTrue(threadRef.get() == thread); 438 assertTrue(exceptionRef.get() instanceof FooException); 439 } 440 441 @Test 442 void testUncaughtExceptionHandler2() throws Exception { 443 class FooException extends RuntimeException { } 444 AtomicReference<Thread> threadRef = new AtomicReference<>(); 445 AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); 446 Thread thread = Thread.ofVirtual() 447 .uncaughtExceptionHandler((t, e) -> { 448 assertTrue(t == Thread.currentThread()); 449 threadRef.set(t); 450 exceptionRef.set(e); 451 }) 452 .start(() -> { throw new FooException(); }); 453 thread.join(); 454 assertTrue(threadRef.get() == thread); 455 assertTrue(exceptionRef.get() instanceof FooException); 456 } 457 458 @Test 459 void testUncaughtExceptionHandler3() throws Exception { 460 class FooException extends RuntimeException { } 461 AtomicReference<Thread> threadRef = new AtomicReference<>(); 462 AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); 463 Thread thread = Thread.ofPlatform() 464 .uncaughtExceptionHandler((t, e) -> { 465 assertTrue(t == Thread.currentThread()); 466 threadRef.set(t); 467 exceptionRef.set(e); 468 }) 469 .factory() 470 .newThread(() -> { throw new FooException(); }); 471 thread.start(); 472 thread.join(); 473 assertTrue(threadRef.get() == thread); 474 assertTrue(exceptionRef.get() instanceof FooException); 475 } 476 477 @Test 478 void testUncaughtExceptionHandler4() throws Exception { 479 class FooException extends RuntimeException { } 480 AtomicReference<Thread> threadRef = new AtomicReference<>(); 481 AtomicReference<Throwable> exceptionRef = new AtomicReference<>(); 482 Thread thread = Thread.ofPlatform() 483 .uncaughtExceptionHandler((t, e) -> { 484 assertTrue(t == Thread.currentThread()); 485 threadRef.set(t); 486 exceptionRef.set(e); 487 }) 488 .factory() 489 .newThread(() -> { throw new FooException(); }); 490 thread.start(); 491 thread.join(); 492 assertTrue(threadRef.get() == thread); 493 assertTrue(exceptionRef.get() instanceof FooException); 494 } 495 496 static final ThreadLocal<Object> LOCAL = new ThreadLocal<>(); 497 static final ThreadLocal<Object> INHERITED_LOCAL = new InheritableThreadLocal<>(); 498 499 /** 500 * Tests that a builder creates threads that support thread locals 501 */ 502 private void testThreadLocals(Thread.Builder builder) throws Exception { 503 AtomicBoolean done = new AtomicBoolean(); 504 Runnable task = () -> { 505 Object value = new Object(); 506 LOCAL.set(value); 507 assertTrue(LOCAL.get() == value); 508 done.set(true); 509 }; 510 511 done.set(false); 512 Thread thread1 = builder.unstarted(task); 513 thread1.start(); 514 thread1.join(); 515 assertTrue(done.get()); 516 517 done.set(false); 518 Thread thread2 = builder.start(task); 519 thread2.join(); 520 assertTrue(done.get()); 521 522 done.set(false); 523 Thread thread3 = builder.factory().newThread(task); 524 thread3.start(); 525 thread3.join(); 526 assertTrue(done.get()); 527 } 528 529 /** 530 * Tests that a builder creates threads that do not support thread locals 531 */ 532 private void testNoThreadLocals(Thread.Builder builder) throws Exception { 533 AtomicBoolean done = new AtomicBoolean(); 534 Runnable task = () -> { 535 try { 536 LOCAL.set(new Object()); 537 } catch (UnsupportedOperationException expected) { 538 done.set(true); 539 } 540 }; 541 542 done.set(false); 543 Thread thread1 = builder.unstarted(task); 544 thread1.start(); 545 thread1.join(); 546 assertTrue(done.get()); 547 548 done.set(false); 549 Thread thread2 = builder.start(task); 550 thread2.join(); 551 assertTrue(done.get()); 552 553 done.set(false); 554 Thread thread3 = builder.factory().newThread(task); 555 thread3.start(); 556 thread3.join(); 557 assertTrue(done.get()); 558 } 559 560 /** 561 * Test Thread.Builder creates threads that allow thread locals. 562 */ 563 @Test 564 void testThreadLocals1() throws Exception { 565 Thread.Builder builder = Thread.ofPlatform(); 566 testThreadLocals(builder); 567 } 568 569 @Test 570 void testThreadLocals2() throws Exception { 571 Thread.Builder builder = Thread.ofVirtual(); 572 testThreadLocals(builder); 573 } 574 575 /** 576 * Tests that a builder creates threads that inherits the initial values of 577 * inheritable thread locals. 578 */ 579 private void testInheritedThreadLocals(Thread.Builder builder) throws Exception { 580 Object value = new Object(); 581 INHERITED_LOCAL.set(value); 582 583 AtomicBoolean done = new AtomicBoolean(); 584 Runnable task = () -> { 585 assertTrue(INHERITED_LOCAL.get() == value); 586 done.set(true); 587 }; 588 589 done.set(false); 590 Thread thread1 = builder.unstarted(task); 591 thread1.start(); 592 thread1.join(); 593 assertTrue(done.get()); 594 595 done.set(false); 596 Thread thread2 = builder.start(task); 597 thread2.join(); 598 assertTrue(done.get()); 599 600 done.set(false); 601 Thread thread3 = builder.factory().newThread(task); 602 thread3.start(); 603 thread3.join(); 604 assertTrue(done.get()); 605 } 606 607 /** 608 * Tests that a builder creates threads that do not inherit the initial values 609 * of inheritable thread locals. 610 */ 611 private void testNoInheritedThreadLocals(Thread.Builder builder) throws Exception { 612 Object value = new Object(); 613 INHERITED_LOCAL.set(value); 614 615 AtomicBoolean done = new AtomicBoolean(); 616 Runnable task = () -> { 617 assertNull(INHERITED_LOCAL.get()); 618 done.set(true); 619 }; 620 621 done.set(false); 622 Thread thread1 = builder.unstarted(task); 623 thread1.start(); 624 thread1.join(); 625 assertTrue(done.get()); 626 627 done.set(false); 628 Thread thread2 = builder.start(task); 629 thread2.join(); 630 assertTrue(done.get()); 631 632 done.set(false); 633 Thread thread3 = builder.factory().newThread(task); 634 thread3.start(); 635 thread3.join(); 636 assertTrue(done.get()); 637 } 638 639 /** 640 * Test Thread.Builder creating threads that inherit or do not inherit 641 * the initial values of inheritable thread locals. 642 */ 643 @Test 644 void testInheritedThreadLocals1() throws Exception { 645 Thread.Builder builder = Thread.ofPlatform(); 646 testInheritedThreadLocals(builder); // default 647 648 // do no inherit 649 builder.inheritInheritableThreadLocals(false); 650 testNoInheritedThreadLocals(builder); 651 652 // inherit 653 builder.inheritInheritableThreadLocals(true); 654 testInheritedThreadLocals(builder); 655 } 656 657 @Test 658 void testInheritedThreadLocals2() throws Exception { 659 Thread.Builder builder = Thread.ofVirtual(); 660 testInheritedThreadLocals(builder); // default 661 662 // do no inherit 663 builder.inheritInheritableThreadLocals(false); 664 testNoInheritedThreadLocals(builder); 665 666 // inherit 667 builder.inheritInheritableThreadLocals(true); 668 testInheritedThreadLocals(builder); 669 } 670 671 /** 672 * Tests a builder creates threads that inherit the context class loader. 673 */ 674 private void testInheritContextClassLoader(Thread.Builder builder) throws Exception { 675 ClassLoader savedCCL = Thread.currentThread().getContextClassLoader(); 676 try { 677 ClassLoader loader = new ClassLoader() { }; 678 Thread.currentThread().setContextClassLoader(loader); 679 680 var ref = new AtomicReference<ClassLoader>(); 681 Runnable task = () -> { 682 ref.set(Thread.currentThread().getContextClassLoader()); 683 }; 684 685 // unstarted 686 Thread thread1 = builder.unstarted(task); 687 assertTrue(thread1.getContextClassLoader() == loader); 688 689 // started 690 ref.set(null); 691 thread1.start(); 692 thread1.join(); 693 assertTrue(ref.get() == loader); 694 695 // factory 696 Thread thread2 = builder.factory().newThread(task); 697 assertTrue(thread2.getContextClassLoader() == loader); 698 699 // started 700 ref.set(null); 701 thread2.start(); 702 thread2.join(); 703 assertTrue(ref.get() == loader); 704 705 } finally { 706 Thread.currentThread().setContextClassLoader(savedCCL); 707 } 708 } 709 710 /** 711 * Tests a builder creates threads does not inherit the context class loader. 712 */ 713 private void testNoInheritContextClassLoader(Thread.Builder builder) throws Exception { 714 ClassLoader savedCCL = Thread.currentThread().getContextClassLoader(); 715 try { 716 Thread.currentThread().setContextClassLoader( new ClassLoader() { }); 717 718 ClassLoader scl = ClassLoader.getSystemClassLoader(); 719 720 var ref = new AtomicReference<ClassLoader>(); 721 Runnable task = () -> { 722 ref.set(Thread.currentThread().getContextClassLoader()); 723 }; 724 725 // unstarted 726 Thread thread1 = builder.unstarted(task); 727 assertTrue(thread1.getContextClassLoader() == scl); 728 729 // started 730 ref.set(null); 731 thread1.start(); 732 thread1.join(); 733 assertTrue(ref.get() == scl); 734 735 // factory 736 Thread thread2 = builder.factory().newThread(task); 737 assertTrue(thread2.getContextClassLoader() == scl); 738 739 // started 740 ref.set(null); 741 thread2.start(); 742 thread2.join(); 743 assertTrue(ref.get() == scl); 744 745 } finally { 746 Thread.currentThread().setContextClassLoader(savedCCL); 747 } 748 } 749 750 /** 751 * Test Thread.Builder creating threads that inherit or do not inherit 752 * the thread context class loader. 753 */ 754 @Test 755 void testContextClassLoader1() throws Exception { 756 Thread.Builder builder = Thread.ofPlatform(); 757 testInheritContextClassLoader(builder); // default 758 759 // do no inherit 760 builder.inheritInheritableThreadLocals(false); 761 testNoInheritContextClassLoader(builder); 762 763 // inherit 764 builder.inheritInheritableThreadLocals(true); 765 testInheritContextClassLoader(builder); 766 } 767 768 @Test 769 void testContextClassLoader2() throws Exception { 770 Thread.Builder builder = Thread.ofVirtual(); 771 testInheritContextClassLoader(builder); // default 772 773 // do no inherit 774 builder.inheritInheritableThreadLocals(false); 775 testNoInheritContextClassLoader(builder); 776 777 // inherit 778 builder.inheritInheritableThreadLocals(true); 779 testInheritContextClassLoader(builder); 780 } 781 782 /** 783 * Test NullPointerException. 784 */ 785 @Test 786 void testNulls1() { 787 Thread.Builder.OfPlatform builder = Thread.ofPlatform(); 788 assertThrows(NullPointerException.class, () -> builder.group(null)); 789 assertThrows(NullPointerException.class, () -> builder.name(null)); 790 assertThrows(NullPointerException.class, () -> builder.name(null, 0)); 791 assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null)); 792 assertThrows(NullPointerException.class, () -> builder.unstarted(null)); 793 assertThrows(NullPointerException.class, () -> builder.start(null)); 794 } 795 796 @Test 797 void testNulls2() { 798 Thread.Builder builder = Thread.ofVirtual(); 799 assertThrows(NullPointerException.class, () -> builder.name(null)); 800 assertThrows(NullPointerException.class, () -> builder.name(null, 0)); 801 assertThrows(NullPointerException.class, () -> builder.uncaughtExceptionHandler(null)); 802 assertThrows(NullPointerException.class, () -> builder.unstarted(null)); 803 assertThrows(NullPointerException.class, () -> builder.start(null)); 804 } 805 }