|
1 | 1 | package io.netty.loom; |
2 | 2 |
|
3 | | -import static org.junit.jupiter.api.Assertions.assertEquals; |
4 | | -import static org.junit.jupiter.api.Assertions.assertFalse; |
5 | | -import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
6 | | -import static org.junit.jupiter.api.Assertions.assertNotEquals; |
7 | | -import static org.junit.jupiter.api.Assertions.assertNotSame; |
8 | | -import static org.junit.jupiter.api.Assertions.assertNull; |
9 | | -import static org.junit.jupiter.api.Assertions.assertSame; |
10 | | -import static org.junit.jupiter.api.Assertions.assertTrue; |
| 3 | +import static java.util.concurrent.StructuredTaskScope.Joiner.allSuccessfulOrThrow; |
| 4 | +import static org.junit.jupiter.api.Assertions.*; |
11 | 5 | import static org.junit.jupiter.api.Assumptions.assumeTrue; |
12 | 6 |
|
13 | | -import java.io.IOException; |
14 | 7 | import java.net.InetSocketAddress; |
15 | | -import java.net.ServerSocket; |
16 | | -import java.net.Socket; |
17 | 8 | import java.net.URI; |
18 | 9 | import java.net.http.HttpClient; |
19 | 10 | import java.net.http.HttpRequest; |
20 | 11 | import java.net.http.HttpResponse; |
21 | | -import java.util.Objects; |
22 | | -import java.util.concurrent.BrokenBarrierException; |
23 | | -import java.util.concurrent.CompletableFuture; |
24 | | -import java.util.concurrent.CountDownLatch; |
25 | | -import java.util.concurrent.CyclicBarrier; |
26 | | -import java.util.concurrent.ExecutionException; |
27 | | -import java.util.concurrent.ExecutorService; |
28 | | -import java.util.concurrent.Executors; |
29 | | -import java.util.concurrent.ThreadFactory; |
30 | | -import java.util.concurrent.TimeUnit; |
31 | | -import java.util.concurrent.TimeoutException; |
| 12 | +import java.util.concurrent.*; |
32 | 13 | import java.util.concurrent.atomic.AtomicBoolean; |
33 | 14 | import java.util.concurrent.atomic.AtomicInteger; |
34 | | -import java.util.concurrent.locks.LockSupport; |
35 | 15 |
|
36 | 16 | import io.netty.channel.Channel; |
37 | 17 | import io.netty.channel.ChannelInboundHandlerAdapter; |
@@ -331,37 +311,43 @@ public void channelRead(io.netty.channel.ChannelHandlerContext ctx, Object msg) |
331 | 311 | group.shutdownGracefully(); |
332 | 312 | } |
333 | 313 |
|
334 | | - @Test |
335 | | - void schedulerIsInherited() throws InterruptedException, ExecutionException { |
336 | | - var group = new VirtualMultithreadIoEventLoopGroup(1, LocalIoHandler.newFactory()); |
337 | | - final Thread expectedCarrier = group.submit(() -> LoomSupport.getCarrierThread(Thread.currentThread())).get(); |
338 | | - final CompletableFuture<Thread> vfactoryCarrier = new CompletableFuture<>(); |
339 | | - group.execute(() -> { |
340 | | - group.vThreadFactory().newThread(() -> { |
341 | | - vfactoryCarrier.complete(LoomSupport.getCarrierThread(Thread.currentThread())); |
342 | | - }).start(); |
343 | | - }); |
344 | | - final CompletableFuture<Thread> inheritedCarrier = new CompletableFuture<>(); |
345 | | - group.execute(() -> { |
346 | | - group.vThreadFactory().newThread(() -> { |
347 | | - Thread.ofVirtual().start(() -> { |
348 | | - inheritedCarrier.complete(LoomSupport.getCarrierThread(Thread.currentThread())); |
349 | | - }); |
350 | | - }).start(); |
351 | | - }); |
352 | | - final CompletableFuture<Thread> inheritedVFactoryCarrier = new CompletableFuture<>(); |
353 | | - group.execute(() -> { |
354 | | - group.vThreadFactory().newThread(() -> { |
355 | | - Thread.ofVirtual().factory().newThread(() -> { |
356 | | - inheritedVFactoryCarrier.complete(LoomSupport.getCarrierThread(Thread.currentThread())); |
| 314 | + @Test |
| 315 | + void schedulerIsNotInheritedWithThreadOfVirtual() throws InterruptedException, ExecutionException { |
| 316 | + try (var group = new VirtualMultithreadIoEventLoopGroup(1, LocalIoHandler.newFactory())) { |
| 317 | + final var expectedScheduler = group.submit(() -> EventLoopScheduler.currentThreadSchedulerContext().scheduler().get()).get(); |
| 318 | + assertNotNull(expectedScheduler); |
| 319 | + final var vThreadFactory = group.submit(group::vThreadFactory).get(); |
| 320 | + var groupEventLoopScheduler = new CompletableFuture<EventLoopScheduler>(); |
| 321 | + var noEventLoopScheduler = new CompletableFuture<>(); |
| 322 | + vThreadFactory.newThread(() -> { |
| 323 | + Thread.ofVirtual().start(() -> noEventLoopScheduler.complete(EventLoopScheduler.currentThreadSchedulerContext().scheduler())); |
| 324 | + groupEventLoopScheduler.complete(EventLoopScheduler.currentThreadSchedulerContext().scheduler().get()); |
357 | 325 | }).start(); |
358 | | - }).start(); |
359 | | - }); |
360 | | - assertEquals(expectedCarrier, vfactoryCarrier.get()); |
361 | | - assertEquals(expectedCarrier, inheritedCarrier.get()); |
362 | | - assertEquals(expectedCarrier, inheritedVFactoryCarrier.get()); |
363 | | - group.shutdownGracefully(); |
364 | | - } |
| 326 | + assertEquals(expectedScheduler, groupEventLoopScheduler.get()); |
| 327 | + assertNull(noEventLoopScheduler.get()); |
| 328 | + } |
| 329 | + } |
| 330 | + |
| 331 | + @Test |
| 332 | + void schedulerIsInheritedByForkedVTFromTheRightFactory() throws InterruptedException, ExecutionException { |
| 333 | + try (var group = new VirtualMultithreadIoEventLoopGroup(1, LocalIoHandler.newFactory())) { |
| 334 | + final var expectedEventLoopScheduler = group.submit(() -> EventLoopScheduler.currentThreadSchedulerContext().scheduler().get()).get(); |
| 335 | + assertNotNull(expectedEventLoopScheduler); |
| 336 | + final var vThreadFactory = group.submit(group::vThreadFactory).get(); |
| 337 | + var forkInheritedScheduler = new CompletableFuture<EventLoopScheduler>(); |
| 338 | + vThreadFactory.newThread(() -> { |
| 339 | + try (var scope = StructuredTaskScope.open(allSuccessfulOrThrow(), |
| 340 | + cf -> cf.withThreadFactory(vThreadFactory))) { |
| 341 | + var task = scope.fork(() -> EventLoopScheduler.currentThreadSchedulerContext().scheduler().get()); |
| 342 | + scope.join(); |
| 343 | + forkInheritedScheduler.complete(task.get()); |
| 344 | + } catch (InterruptedException e) { |
| 345 | + forkInheritedScheduler.completeExceptionally(e); |
| 346 | + } |
| 347 | + }).start(); |
| 348 | + assertEquals(expectedEventLoopScheduler, forkInheritedScheduler.get()); |
| 349 | + } |
| 350 | + } |
365 | 351 |
|
366 | 352 | @Test |
367 | 353 | void eventLoopSchedulerCanMakeProgressIfTheEventLoopIsBlocked() throws BrokenBarrierException, InterruptedException, TimeoutException { |
@@ -422,16 +408,20 @@ private static void spinWait(long nanos) { |
422 | 408 | } |
423 | 409 | } |
424 | 410 |
|
425 | | - /* |
426 | | -
|
427 | | - @Test |
428 | | - public void testContainsJustBuiltinPollers() throws InterruptedException { |
429 | | - assertContainsJustBuiltinPollers(builtinGlobalScheduler()); |
430 | | - } |
431 | | -
|
| 411 | + /* |
432 | 412 | @Test |
433 | 413 | public void testPlatformThreadSpawnsVirtualThreads() throws InterruptedException { |
434 | | - testPlatformThreadSpawnsVirtualThreads(builtinGlobalScheduler()); |
| 414 | + CompletableFuture<Thread.VirtualThreadScheduler> schedulerCompletableFuture = new CompletableFuture<>(); |
| 415 | + Thread.ofPlatform() |
| 416 | + .start(() -> { |
| 417 | + Thread.ofVirtual() |
| 418 | + .start(() -> { |
| 419 | + Thread.VirtualThreadScheduler virtualThreadScheduler = NettyScheduler.internalSchedulerMappings.get(Thread.currentThread()); |
| 420 | + schedulerCompletableFuture.complete(virtualThreadScheduler); |
| 421 | + }); |
| 422 | + }); |
| 423 | + Thread.VirtualThreadScheduler virtualThreadScheduler = schedulerCompletableFuture.join(); |
| 424 | + assertEquals(NettyScheduler.jdkBuildinScheduler, virtualThreadScheduler); |
435 | 425 | } |
436 | 426 |
|
437 | 427 | @Test |
@@ -588,20 +578,6 @@ private static void assertContainsJustBuiltinPollers(NettyScheduler NettySchedul |
588 | 578 | entry.getValue() == NettyScheduler.getJdkBuildinScheduler())); |
589 | 579 | } |
590 | 580 |
|
591 | | - private void testPlatformThreadSpawnsVirtualThreads(NettyScheduler NettyScheduler) { |
592 | | - CompletableFuture<Thread.VirtualThreadScheduler> schedulerCompletableFuture = new CompletableFuture<>(); |
593 | | - Thread.ofPlatform() |
594 | | - .start(() -> { |
595 | | - Thread.ofVirtual() |
596 | | - .start(() -> { |
597 | | - Thread.VirtualThreadScheduler virtualThreadScheduler = NettyScheduler.internalSchedulerMappings.get(Thread.currentThread()); |
598 | | - schedulerCompletableFuture.complete(virtualThreadScheduler); |
599 | | - }); |
600 | | - }); |
601 | | - Thread.VirtualThreadScheduler virtualThreadScheduler = schedulerCompletableFuture.join(); |
602 | | - assertEquals(NettyScheduler.jdkBuildinScheduler, virtualThreadScheduler); |
603 | | - } |
604 | | -
|
605 | 581 | private void testNettyThreadSpawnsVirtualThreads(NettyScheduler NettyScheduler) throws InterruptedException { |
606 | 582 | record NettyVirtualThreadContext(Thread ioEventLoopThread, |
607 | 583 | Thread.VirtualThreadScheduler vtScheduler,Thread.VirtualThreadScheduler vtSchedulerFromScopeValue, |
|
0 commit comments