Slide 1

Slide 1 text

Loom nous Protègera-t-il du Braquage Temporel ? Vaste question ! José Paumard Java Developer Advocate Java Platform Group Rémi Forax Maître de Conférences Université Gustave Eiffel

Slide 2

Slide 2 text

https://twitter.com/JosePaumard https://github.com/JosePaumard https://www.youtube.com/user/java https://www.youtube.com/user/JPaumard https://www.youtube.com/c/coursenlignejava https://www.youtube.com/hashtag/jepcafe https://fr.slideshare.net/jpaumard https://www.pluralsight.com/authors/jose-paumard

Slide 3

Slide 3 text

https://twitter.com/He bah non! https://github.com/forax https://speakerdeck.com/forax OpenJDK, ASM, Tatoo, Pro, etc… One of the Father of invokedynamic (Java 7) Lambda (Java 8), Module (Java 9) Constant dynamic (Java 11) Record (Java 14 / 15) Valhalla (Java 19+)

Slide 4

Slide 4 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 4 https://dev.java/

Slide 5

Slide 5 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 5 Don’t believe what you will hear in this talk! Loom is a Work in Progress

Slide 6

Slide 6 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential: Internal/Restricted/Highly Restricted 6 It all Started with a Runnable…

Slide 7

Slide 7 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 7 1995: Thread, Runnable Threads and Runnables Runnable task = new Runnable() { void run() { System.out.println("I am running in thread " + Thread.currentThread().getName()); } }; Thread thread = new Thread(task); thread.start(); thread.join(); // blocks

Slide 8

Slide 8 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 8 1995: Thread, Runnable Threads and Runnables Object key = new Object(); synchronized(key) { System.out.println("Only one thread can execute me!"); }

Slide 9

Slide 9 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 9 2004: Java 5, java.util.concurrent Java Util Concurrent Callable task = new Callable() { @Override public String call() { return "I am running in thread " + Thread.currentThread().getName(); } };

Slide 10

Slide 10 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 10 2004: Java 5, java.util.concurrent Wait lists inside! Java Util Concurrent ExecutorService service = Executors.newFixedThreadPool(4); Future future = service.submit(task);

Slide 11

Slide 11 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 11 2004: Java 5, java.util.concurrent Java Util Concurrent String result = future.get(); // blocks String result = future.get(10, TimeUnit.MICROSECONDS); boolean cancelled = future.cancel(true);

Slide 12

Slide 12 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 12 2004: Java 5, java.util.concurrent Java Util Concurrent Lock lock = new ReentrantLock(); lock.lock(); try { System.out.println("Only one thread can execute me!"); } finally { lock.unlock(); }

Slide 13

Slide 13 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 13 2004: Java 5, java.util.concurrent Plus many more concurrent classes: - Lock, Semaphore, Barrier, CountDownLatch - BlockingQueue, ConcurrentMap - CopyOnWriteArrayList Java Util Concurrent

Slide 14

Slide 14 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 14 2011 – 2014 (Java 7, Java 8): - Fork / Join, parallel Stream Allows to compute elements in parallel Two phases: - fork = splits a task in two sub-tasks - join = merge the result of two sub-tasks Uses work stealing to spread the tasks among threads Fork / Join

Slide 15

Slide 15 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 15 2011 – 2014 (Java 7, Java 8): - CompletionStage, CompletableFuture Subtype of Future Asynchronous programming model Allows to trigger tasks on the outcome of other tasks User can control which thread executes what task Exceptions handling CompletionStage

Slide 16

Slide 16 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 16 Once a thread begins to process a task it cannot release it Either the task completes with a result Or is completes with an exception It may be an InterruptedException One thing stays the same

Slide 17

Slide 17 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 17 2022+ (Java 19 or later): Loom! Loom!

Slide 18

Slide 18 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential: Internal/Restricted/Highly Restricted 18 What is Loom Addressing?

Slide 19

Slide 19 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 19 Concurrency may be used in two different contexts: - processing in-memory data in parallel, using all the CPU cores - handling numerous blocking requests / responses Concurrency: Computations vs. I/O

Slide 20

Slide 20 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 20 Computing in parallel: - Each thread uses 100% of your CPU cores - Threads are mostly not blocking No need to have more threads than (physical) CPU cores Concurrency for Computation

Slide 21

Slide 21 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 21 Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Preparing the request Time scale: 10ns

Slide 22

Slide 22 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 22 Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Waiting for the response Time scale: 10ms

Slide 23

Slide 23 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 23 Processing I/O data: - Each task waits for the data it needs to process Concurrency for I/O Processing the response Time scale: 10ns

Slide 24

Slide 24 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 24 Processing I/O data: A Thread is idle 99.9999% of the time! How many threads do you need to keep your CPU busy? Concurrency for I/O ms ns ns

Slide 25

Slide 25 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 25 Because a thread cannot switch from one task to the other, the only solution is to have more threads But a thread is not cheap! - Thread startup time: ~1ms - Thread memory consumption: 2MB of stack - Context switching: ~100ms (depends on the OS) Concurrency for I/O

Slide 26

Slide 26 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 26 How Many Threads Can I Run? var threads = IntStream.range(0, 100_000) .mapToObj(i -> new Thread(() -> { try { Thread.sleep(5_000); } catch (InterruptedException e) { throw new AssertionError(e); } })) .toList();

Slide 27

Slide 27 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 27 How Many Threads Can I Run? var i = 0; for (var thread: threads) { System.out.println(i++); thread.start(); } for (var thread : threads) { thread.join(); }

Slide 28

Slide 28 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 28 How Many Threads Can I Run? ... 4065 4066 4067 4068 [0.373s][warning][os,thread] Failed to start thread "Unknown thread" - pthread_create failed (EAGAIN) for attributes: stacksize: 2048k, guardsize: 16k, detached. [0.373s][warning][os,thread] Failed to start the native thread for java.lang.Thread "Thread- 4066" Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread: possibly out of memory or process/resource limits reached at java.base/java.lang.Thread.start0(Native Method) at java.base/java.lang.Thread.start(Thread.java:1451) at _3_how_many_platform_thread.printHowManyThreads(...java:19) at _3_how_many_platform_thread.main(...java:46) On a MacBook Air M1 (16GB of RAM)

Slide 29

Slide 29 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 29 OS/Platform threads are not cheap ! What if I’ve more than 4068 clients for my web server ?

Slide 30

Slide 30 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 30 Need to change the model 1 request <=|=> 1 thread

Slide 31

Slide 31 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 31 Paradigmatic change Asynchronous/Reactive programming CompletableFuture (JDK) Async/await (C# or Kotlin) Mono/Flux (Spring) or Uni/Multi (Quarkus)

Slide 32

Slide 32 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 32 Paradigmatic change Asynchronous/Reactive programming But:  debugging is harder  profiling is harder  testing is harder + colored function problem

Slide 33

Slide 33 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 33 Other solution ⇒ user-level thread … like Erlang process / Golang goroutine Official name in Java, virtual threads

Slide 34

Slide 34 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 34 DEMO

Slide 35

Slide 35 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 35 Virtual Thread // platform threads var pthread = new Thread(() -> { System.out.println("platform " + Thread.currentThread()); }); pthread.start(); pthread.join(); // virtual threads var vthread = Thread.startVirtualThread(() -> { System.out.println("virtual " + Thread.currentThread()); }); vthread.join();

Slide 36

Slide 36 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 36 Virtual Thread // platform threads platform Thread[#14,Thread-0,5,main] // virtual threads virtual VirtualThread[#15]/runnable@ForkJoinPool-1-worker-1 Use a dedicated fork-join thread pool internally Warning! This pool is not the common fork join pool

Slide 37

Slide 37 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 37 Using a polymorphic builder

Slide 38

Slide 38 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 38 Thread Builder // platform threads var pthread = Thread.ofPlatform() .name("platform-", 0) .start(() -> { System.out.println("platform " + Thread.currentThread()); }); pthread.join(); // virtual threads var vthread = Thread.ofVirtual() .name("virtual-", 0) .start(() -> { System.out.println("virtual " + Thread.currentThread()); }); vthread.join();

Slide 39

Slide 39 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 39 How many virtual threads can I run?

Slide 40

Slide 40 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 40 DEMO

Slide 41

Slide 41 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 41 How Many Virtual Threads Can I Run? var counter = new AtomicInteger(); var threads = IntStream.range(0, 100_000) .mapToObj(i -> Thread.ofVirtualThread().unstarted()() -> { try { Thread.sleep(5_000); } catch (InterruptedException e) { throw new AssertionError(e); } counter.incrementAndGet(); })) .toList(); for (var thread: threads) { thread.start(); } for (var thread: threads) { thread.join(); } System.out.println(i++);

Slide 42

Slide 42 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 42 Platform/OS thread (starts in ms) - Creates a 2MB stack upfront - System call to ask the OS to schedule the thread Virtual thread (starts in μs) - Grow and shrink the stack dynamically - Use a specific fork-join pool of platform threads (carrier threads) - One platform thread per core Running a Thread

Slide 43

Slide 43 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 43 How does it work under the hood?

Slide 44

Slide 44 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 44 Two strategies for concurrency - Competitive: all threads compete for the CPUs/cores - Cooperative: each thread hand of the CPUs to the next Loom implementation does both - OS schedule carrier threads to CPU/cores and - JDK codes schedule virtual threads to carrier threads Loom Internals

Slide 45

Slide 45 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 45 DEMO

Slide 46

Slide 46 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 46 jdk.internal.vm.Continuation var scope = new ContinuationScope("hello"); var continuation = new Continuation(scope, () -> { System.out.println("C1"); Continuation.yield(scope); System.out.println("C2"); Continuation.yield(scope); System.out.println("C3"); }); System.out.println("start"); continuation.run(); System.out.println("came back"); continuation.run(); System.out.println("back again"); continuation.run(); System.out.println("back again again"); Execution: start C1 came back C2 back again C3 back again again

Slide 47

Slide 47 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 47 yield() copies the stack to the heap Continuation.yield() heap stack start() Platform thread

Slide 48

Slide 48 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 48 run() copies from the heap to another stack (optimization: only copies the topmost stack frames) Continuation.run() stack start() Platform thread 1 stack Platform thread 2 heap

Slide 49

Slide 49 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 49 Most of the code of the virtual threads scheduling is written in Java in the JDK (jdk.internal.vm.Continuation) Written in C in the JVM: - Copy of the stack frames back and forth - GCs modified to find references in stack on heap Loom is not Implemented « By the JVM »

Slide 50

Slide 50 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 50 All blocking codes are changed to - Check if current thread is a virtual thread - If it is, instead of blocking: - Register a handler that will be called when the OS is ready (using NIO) - When the handler is called, find a carrier thread and called Continuation.start() - Call Continuation.yield() In the JDK

Slide 51

Slide 51 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 51 Example with Thread.sleep() private static void sleepMillis(long millis) throws InterruptedException { Thread thread = currentThread(); if (thread instanceof VirtualThread vthread) { long nanos = NANOSECONDS.convert(millis, MILLISECONDS); vthread.sleepNanos(nanos); } else { sleep0(millis); } } void sleepNanos(long nanos) throws ... long remainingNanos = ...; while (remainingNanos > 0) { parkNanos(remainingNanos); ... } } void parkNanos(long nanos) { long startTime = System.nanoTime(); boolean yielded; Future> unparker = scheduleUnpark(nanos); setState(PARKING); try { yielded = yieldContinuation(); } finally { cancel(unparker); } // park on the carrier thread for remaining time when pinned if (!yielded) { parkOnCarrierThread(true, deadline - System.nanoTime()); } }

Slide 52

Slide 52 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 52 Synchronized block are written in assembly and uses an address on stack the stack frames can not be copied Native code that does an upcall to Java may use an address on stack the stack frames can not be copied yield() Can Fail!

Slide 53

Slide 53 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 53 Java 13 - JEP 353 Reimplement the Legacy Socket API Java 14 - JEP 373 Reimplement the Legacy DatagramSocket API - JEP 374 Deprecate and Disable Biased Locking Stealth Rewrite of the JDK for Loom

Slide 54

Slide 54 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 54 Java 18 - JEP 416 Reimplement Core Reflection with Method Handles - JEP 418 (Internet-Address Resolution SPI) in JDK 18 defined a service-provider interface for host name and address lookup. This will allow third-party libraries to implement alternative java.net.InetAddress resolvers that do not pin threads during host lookup Stealth Rewrite of the JDK for Loom

Slide 55

Slide 55 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 55 The JDK creates as many virtual threads as the user want - Mount a virtual thread to an available carrier thread when starting - If blocking, unmount the current virtual thread and mount another virtual thread Loom Idea: Under the Hood

Slide 56

Slide 56 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 56 Synchronized blocks (around slow/frequent IOs) use ReentrantLock instead Native code that does an upcall no such call in the JDK anymore Problems with some external libraries using native codes: Hadoop, Spark, ... There are Still Some Issues

Slide 57

Slide 57 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 57 Loom implementation allows a thread to switch from one task to another

Slide 58

Slide 58 text

Coffee (or whatever) Break!

Slide 59

Slide 59 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential: Internal/Restricted/Highly Restricted 59 Structured Concurrency

Slide 60

Slide 60 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 60 A travel agency sells travels. On the response page, it wants to display: - the quotation - the weather forecast for the destination The Travel Agency Example

Slide 61

Slide 61 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 61 The Travel Agency Example Quotation Weather Forecast Travel Page

Slide 62

Slide 62 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 62 1) If getQuotation() fails, getTravelPage() fails and getWeather() is not called Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); }

Slide 63

Slide 63 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 63 2) If the getTravelPage() thread is interrupted, everything is interrupted. Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); }

Slide 64

Slide 64 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 64 3) A stack trace shows you that you are running getQuotation(), called by getTravelPage(). Synchronous Travel Agency Page getTravelPage() { Quotation quotation = getQuotation(); // exception? Weather weather = getWeather(); // exception? return buildPage(quotation, weather); } org.paumard.loom.LoomExample.getQuotation(LoomExample.java:99) org.paumard.loom.LoomExample.getTravelPage(LoomExample.java:20) org.paumard.loom.LoomExample.main(LoomExample.java:16)

Slide 65

Slide 65 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 65 Problems: - Latency grows with the number of requests - Your CPU is used at 0.00001% Synchronous Travel Agency

Slide 66

Slide 66 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 66 ExecutorService Based Travel Agency ExecutorService service = ...; Page getTravelPage() { Future quotation = service.submit(() -> getQuotation()); Future weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }

Slide 67

Slide 67 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 67 1) If getQuotation() fails, getTravelPage() fails, but getWeather() is still running ExecutorService Based Travel Agency Future quotation = service.submit(() -> getQuotation()); Future weather = service.submit(() -> getWeather());

Slide 68

Slide 68 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 68 2) If getTravelPage() is interrupted, then getQuotation() and getWeather() don’t know it. ExecutorService Based Travel Agency Page getTravelPage() { Future quotation = service.submit(() -> getQuotation()); Future weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }

Slide 69

Slide 69 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 69 3) What about the stack trace? ExecutorService Based Travel Agency Page getTravelPage() { Future quotation = service.submit(() -> getQuotation()); Future weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }

Slide 70

Slide 70 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 70 Stack trace org.paumard.loom.LoomExample.getQuotation(LoomExample.java:107) org.paumard.loom.LoomExample.lambda$getTravelPage$0(LoomExample.java:28) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143) org.paumard.loom.LoomExample.getQuotation(LoomExample.java:110) org.paumard.loom.LoomExample.lambda$static$0(LoomExample.java:25) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143) org.paumard.loom.LoomJunk.getQuotation(LoomJunk.java:110) java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317) java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1143)

Slide 71

Slide 71 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 71 CompletableFuture Based Travel Agency var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather()); CompletableFuture travelPageCF = quotationCF.thenCompose( quotation -> weatherCF .thenApply( weather -> buildPage(quotation, weather)));

Slide 72

Slide 72 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 72 1) If getQuotation() fails, getTravelPage() fails, and getWeather() is still called CompletableFuture Based Travel Agency var quotationCF = CompletableFuture.supplyAsync(() -> getQuotation()); var weatherCF = CompletableFuture.supplyAsync(() -> getWeather());

Slide 73

Slide 73 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 73 1) If getQuotation() fails, getTravelPage() fails, you can cancel getWeather() CompletableFuture Based Travel Agency CompletableFuture travelPageCF = quotationCF .exceptionally(t -> { weatherCF.cancel(true); throw new RuntimeException(t); }) .thenCompose(...);

Slide 74

Slide 74 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 74 2) If getWeather() fails, you can handle the exception nicely CompletableFuture Based Travel Agency CompletableFuture travelPageCF = quotationCF.thenCompose( quotation -> weatherCF // .completeOnTimeout(Weather.UNKNOWN, 100, MILLISECONDS) .exceptionally(e -> Weather.UNKNOWN) .thenApply( weather -> buildPage(quotation, weather)));

Slide 75

Slide 75 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 75 3) If getTravelPage() is interrupted, you cannot interrupt getQuotation() nor getWeather() 4) And what about the stack trace? CompletableFuture Based Travel Agency

Slide 76

Slide 76 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 76 CompletableFuture Based Travel Agency org.paumard.loom.LoomExample.getQuotation(LoomExample.java:125) org.paumard.loom.LoomExample.lambda$getTravelPage$2(LoomExample.java:38) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) org.paumard.loom.LoomExample.getWeather(LoomExample.java:123) org.paumard.loom.LoomExample.lambda$getTravelPage$3(LoomExample.java:40) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768) java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1760) java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)

Slide 77

Slide 77 text

Quotation Server A 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 77 The Travel Agency Example Quotation Server B Quotation Server C Weather Forecast Server A Weather Forecast Server B Weather Forecast Server C Travel Agency

Slide 78

Slide 78 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 78 The lifespan of a scope is bound by a try-with-resources Loom : Structuring a Scope try (var scope = new StructuredTaskScope<>()) { } // scope.close() is called

Slide 79

Slide 79 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 79 Join splits the scope in two parts: launching the tasks and getting the results Loom : Structuring a Scope try (var scope = new StructuredTaskScope<>()) { // launching the tasks scope.join(); // blocks until tasks are computed // getting the results }

Slide 80

Slide 80 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 80 A task is launched with the fork() method of a scope Each task is executed in its own virtual thread Loom : Launching a Task try (var scope = new StructuredTaskScope<>()) { // launching the tasks Future quotation = scope.fork(() -> getQuotation()); scope.join(); // ... }

Slide 81

Slide 81 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 81 A result is obtained by calling resultNow() Loom : Getting the Result try (var scope = new StructuredTaskScope<>()) { // ... scope.join(); // getting the results Quotation result = quotation.resultNow(); }

Slide 82

Slide 82 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 82 A scope is confined in a method The methods of a StructuredTaskScope must be called by the same thread as the one that created it This is enforced by a runtime exception! Loom : Avoiding API Misuses

Slide 83

Slide 83 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 87 The handleComplete() method is called on a completion of a task You can extend StructuredTaskScope to override it Suppose you want to call several quotation servers and choose the least expensive Loom: Task Completion Callback

Slide 84

Slide 84 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 88 Loom Travel Agency class QuotationScope extends StructuredTaskScope { @Override protected void handleComplete(Future future) { switch (future.state()) { case RUNNING -> throw new IllegalArgumentException("..."); case SUCCESS -> successes.add(future.resultNow()); case FAILED -> failures.add(future); case CANCELLED -> { } } } }

Slide 85

Slide 85 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 89 Loom Travel Agency class QuotationScope extends StructuredTaskScope { public Quotation getQuotation() { return successes.stream() .min(Comparator.comparing(Quotation::quotation)) .orElseThrow(); } }

Slide 86

Slide 86 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 90 Loom Travel Agency class QuotationScope extends StructuredTaskScope { public void throwIfFailed() { if (successes.isEmpty() && failures.isEmpty()) { throw new RuntimeException(...); } if (failures.isEmpty()) { return; } if (successes.isEmpty()) { Future future = failures.poll(); throw new RuntimeException(future.exceptionNow()); } // le faire avec des suppressed exceptions } }

Slide 87

Slide 87 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 91 Loom Travel Agency class QuotationScope extends StructuredTaskScope { public void joinUntil(Instant deadline) { try { super.joinUntil(deadline); } catch (TimeoutException e) { shutdown(); } } }

Slide 88

Slide 88 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 92 Loom Travel Agency public static Quotation computeQuotation() { try (var scope = new QuotationScope()) { scope.fork(() -> getQuotationFromA()); scope.fork(() -> getQuotationFromB()); scope.joinUntil(Instant.now().plusMillis(100)); return scope.getBestQuotation() .orElseThrow(() -> scope.getException()); } }

Slide 89

Slide 89 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 93 What about the weather? Suppose you want to call several weather forecast servers and send the first answer And you don’t want to fail is there is no weather forecast available Loom: Task Completion Callback

Slide 90

Slide 90 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 94 Loom Travel Agency class WeatherScope extends StructuredTaskScope { final AtomicReference firstWeather = new AtomicReference<>(Weather.UNKNOWN); @Override protected void handleComplete(Future future) { switch (future.state()) { case RUNNING -> throw new IllegalArgumentException("..."); case SUCCESS -> { firstWeather.compareAndSet(Weather.UNKNOWN, future.resultNow()); shutdown(); } case FAILED, CANCELLED -> { } } } }

Slide 91

Slide 91 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 95 Loom Travel Agency class WeatherScope extends StructuredTaskScope { public Weather getFirstWeather() { return firstWeather.get(); } }

Slide 92

Slide 92 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 96 Loom Travel Agency public static Weather computeWeather() { try (var scope = new WeatherScope()) { scope.fork(() -> getWeatherFromA()); scope.fork(() -> getWeatherFromB()); scope.joinUntil(Instant.now().plusMillis(100)); return scope.getFirstWeather(); } catch (TimeoutException e) { return Weather.UNKNOWN; } }

Slide 93

Slide 93 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 97 Loom Travel Agency: Error Handling TravelPage getTravelPage() { try (scope) { Quotation quotation = Quotation.computeQuotation(); Weather weather = Weather.computeWeather(); return new TravelPage(quotation, weather); } }

Slide 94

Slide 94 text

5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 98 Allows you to bind elements to a scope Your tasks can read these elements, and use them Use ScopeLocal sparsely and wisely! ScopeLocal

Slide 95

Slide 95 text

Coffee Time!