Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Loom nous protègera-t-il du braquage temporel ?

Loom nous protègera-t-il du braquage temporel ?

Slides de notre présentation avec Rémi Forax pour Devoxx France 2022. Vous pouvez revoir la présentation ici : https://youtu.be/wx7t69DylsI et le code de démo ici : https://github.com/JosePaumard/Loom_demo

En travaux depuis plusieurs années, le projet Loom n'a jamais été aussi proche d'être intégré dans la plateforme Java. Des préversions sont disponibles, qui nous permettent déjà d'imaginer les patterns de code et les performances apportées par ce projet. Une nouvelle notion de Thread arrive pour la plateforme Java, ce qui n'est pas une mince affaire. Nos vieux Threads (25 ans) laisseront la place à de nouveaux Threads, plus légers et plus nombreux. Ces nouveaux threads, virtuels, amènent de nouvelles notions, objets de cette présentation, dont la programmation concurrente structurée. Nous parlerons de la programmation réactive et asynchrone, d'acteurs, de ScopeLocal, nouvelle version de ThreadLocal, de StructuredExecutor et verrons les modèles de synchronization apportés par Loom. On montrera du code, on parlera des impacts que Loom aura sur notre travail de développeur au quotidien, et l'on montrera les gains en performance sur nos applications de même que sur la machine virtuelle.

F784a8de872a5d98f67ac5ccbd6683d0?s=128

José

May 14, 2022
Tweet

More Decks by José

Other Decks in Programming

Transcript

  1. 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
  2. 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

  3. 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+)
  4. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 4

    https://dev.java/
  5. 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
  6. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 6 It all Started with a Runnable…
  7. 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
  8. 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!"); }
  9. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 9

    2004: Java 5, java.util.concurrent Java Util Concurrent Callable<String> task = new Callable<String>() { @Override public String call() { return "I am running in thread " + Thread.currentThread().getName(); } };
  10. 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<String> future = service.submit(task);
  11. 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);
  12. 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(); }
  13. 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
  14. 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
  15. 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
  16. 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
  17. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 17

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

    Internal/Restricted/Highly Restricted 18 What is Loom Addressing?
  19. 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
  20. 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
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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
  26. 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();
  27. 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(); }
  28. 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)
  29. 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 ?
  30. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 30

    Need to change the model 1 request <=|=> 1 thread
  31. 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)
  32. 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
  33. 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
  34. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 34

    DEMO
  35. 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();
  36. 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
  37. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 37

    Using a polymorphic builder
  38. 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();
  39. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 39

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

    DEMO
  41. 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++);
  42. 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
  43. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 43

    How does it work under the hood?
  44. 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
  45. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 45

    DEMO
  46. 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
  47. 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
  48. 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
  49. 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 »
  50. 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
  51. 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()); } }
  52. 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!
  53. 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
  54. 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
  55. 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
  56. 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
  57. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 57

    Loom implementation allows a thread to switch from one task to another
  58. Coffee (or whatever) Break!

  59. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | Confidential:

    Internal/Restricted/Highly Restricted 59 Structured Concurrency
  60. 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
  61. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 61

    The Travel Agency Example Quotation Weather Forecast Travel Page
  62. 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); }
  63. 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); }
  64. 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)
  65. 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
  66. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 66

    ExecutorService Based Travel Agency ExecutorService service = ...; Page getTravelPage() { Future<Quotation> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  67. 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> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather());
  68. 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> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  69. 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> quotation = service.submit(() -> getQuotation()); Future<Weather> weather = service.submit(() -> getWeather()); return buildPage(quotation.get(), weather.get()); }
  70. 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)
  71. 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<Page> travelPageCF = quotationCF.thenCompose( quotation -> weatherCF .thenApply( weather -> buildPage(quotation, weather)));
  72. 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());
  73. 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<Page> travelPageCF = quotationCF .exceptionally(t -> { weatherCF.cancel(true); throw new RuntimeException(t); }) .thenCompose(...);
  74. 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<Page> travelPageCF = quotationCF.thenCompose( quotation -> weatherCF // .completeOnTimeout(Weather.UNKNOWN, 100, MILLISECONDS) .exceptionally(e -> Weather.UNKNOWN) .thenApply( weather -> buildPage(quotation, weather)));
  75. 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
  76. 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)
  77. 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
  78. 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
  79. 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 }
  80. 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> quotation = scope.fork(() -> getQuotation()); scope.join(); // ... }
  81. 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(); }
  82. 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
  83. 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
  84. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 88

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

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

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

    Loom Travel Agency class QuotationScope extends StructuredTaskScope<Quotation> { public void joinUntil(Instant deadline) { try { super.joinUntil(deadline); } catch (TimeoutException e) { shutdown(); } } }
  88. 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()); } }
  89. 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
  90. 5/14/2022 Copyright © 2021, Oracle and/or its affiliates | 94

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

    Loom Travel Agency class WeatherScope extends StructuredTaskScope<Weather> { public Weather getFirstWeather() { return firstWeather.get(); } }
  92. 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; } }
  93. 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); } }
  94. 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
  95. Coffee Time!