0

I am just getting started at playing with Project Loom...

Given the Java code which seems to work correctly

try (ExecutorService executor = Executors.newVirtualThreadExecutor()) {
    IntStream.range(0, 16).forEach(i -> {
        System.out.println("i = " + i + ", Thread ID = " + Thread.currentThread());
        executor.submit(() -> {
            var thread = Thread.currentThread();
            System.out.println("Thread ID = " + thread);
        });
    });
}

When IntelliJ converts this to Kotlin I get

Executors.newVirtualThreadExecutor().use { executor ->
    IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
        println("i = $i, Thread ID = ${Thread.currentThread()}")
        executor.submit(Runnable {
            println("Thread ID = ${Thread.currentThread()}")
        })
    })
}

private fun ExecutorService.use(block: (ExecutorService) -> Unit) {}

which seems to compile fine, but when executed, nothing is printed on the console?

Is there some latent incompatibility between Kotlin and Project Loom?

Further experiments show that

Executors.newVirtualThreadExecutor()
    .submit(Runnable { println("Thread = ${Thread.currentThread()}") })

does not print anything, but

Executors.newCachedThreadPool()
    .submit(Runnable { println("Thread = ${Thread.currentThread()}") })

does print the expected result, so there is something fundamentally incompatible between Kotlin and Virtual Threads. However,

Executors.newCachedThreadPool().use { executor ->
    IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
        println("i = $i, Thread ID = ${Thread.currentThread()}")
        executor.submit(Runnable {
            println("Thread ID = ${Thread.currentThread()}")
        })
    })
}

does not print anything, so there are other issues at play too... Indeed

Executors.newCachedThreadPool().use { executor ->
    IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
        println("i = $i, Thread ID = ${Thread.currentThread()}")
    })
}

does not print anything, but

IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
    println("i = $i, Thread ID = ${Thread.currentThread()}")
})

does print the expected results? Also,

IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
    println("i = $i, Thread ID = ${Thread.currentThread()}")
    executor.submit(Runnable {
        println("Thread ID = ${Thread.currentThread()}")
    })
})

prints the expected results, so there is some weirdness with use?

This begs the questions

  1. Are there some problems with how Project Loom is designed/implemented that is causing problems for Kotlin?
    • Are there some problems with JDK-18 that is not compatible with Kotlin?
  2. Are there some problems with how Kotlin is designed/implemented that cannot interoperate with Project Loom?
    • Are there some problems with Kotlin, that are not compatible with JDK-18?
Eric Kolotyluk
  • 1,958
  • 2
  • 21
  • 30
  • I'd highly recommend using Kotlin features over straight converted Java code. – undermark5 Oct 28 '21 at 15:42
  • What's inside your `ExecutorService.use` function? Actually, in the current Loom JDK build, [ExecutorService](https://download.java.net/java/early_access/loom/docs/api/java.base/java/util/concurrent/ExecutorService.html) implements `AutoCloseable`, so the [`use`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html) function from Kotlin stdlib should work fine with it. – Михаил Нафталь Oct 28 '21 at 15:45
  • Wil it work if your replace `newVirtualThreadExecutor()` with, for instance, `newCachedThreadPool()`? – Михаил Нафталь Oct 28 '21 at 15:46
  • @МихаилНафталь thanks for the suggestion, but that didn't help. – Eric Kolotyluk Oct 28 '21 at 16:45
  • @undermark5 - These are experiments... the point is not to use Kotlin features, the point is to answer the questions, can Kotlin work with Project Loom? – Eric Kolotyluk Oct 28 '21 at 16:47
  • `newCachedThreadPool()` works fine on my machine with JDK11, so, probably, the problem is the unstable build of JDK18 used in the Loom JDK build, or in the patches of Loom on top of it itself. Note, that Project Loom is still experimental and it's too early to talk about its interop with other JVM languages. – Михаил Нафталь Oct 28 '21 at 20:01
  • Kotlin has [coroutines](https://kotlinlang.org/docs/coroutines-basics.html), use them. Most likely, when Project Loom gets stabilized, Kotlin developers will continue using coroutines (but behind the scenes, they would be compiled into calls to Loom primitives for the sake of expected performance boost) – Михаил Нафталь Oct 28 '21 at 20:06
  • @МихаилНафталь - yes, I am going to experiment with `java.util.concurrent` in earlier versions such as 17 to try to narrow down where things are going wrong. Yes, Loom is still experimental, and so I am experimenting too . I am aware of coroutines, and have used them. I would hope they switch to Virtual Threads when things are stable. I am not working on any specific project, rather this is just 'recreational programming' – Eric Kolotyluk Oct 28 '21 at 20:50

1 Answers1

1

The problem was I was not defining the use extension function properly. The simple fix is

private fun ExecutorService.use(block: (executorService: ExecutorService) -> Unit) = block(this)

So, now

Executors.newVirtualThreadExecutor().use { executorService ->
    IntStream.range(0, 16).forEach(IntConsumer { i: Int ->
        println("i = $i, Thread ID = ${Thread.currentThread()}")
        executorService.submit(Runnable {
            println("Thread ID = ${Thread.currentThread()}")
        })
    })
}

produces output similar to the Java example

i = 1, Thread ID = Thread[#1,main,5,main]
i = 2, Thread ID = Thread[#1,main,5,main]
Thread ID = VirtualThread[#19]/runnable@ForkJoinPool-1-worker-1
Thread ID = VirtualThread[#20]/runnable@ForkJoinPool-1-worker-2
i = 3, Thread ID = Thread[#1,main,5,main]

I often find it frustrating when IntelliJ automatically creates extension functions because it's not immediately obvious to me what to put in the body, and in this case I did nothing, which was wrong. In spite of much Googling around, I could not easily find code that let me to the solutions, so it was kinda hit-and-miss...

Conclusion, it should be possible to use Project Loom from Kotlin, once you know some special black magic...

lkatiforis
  • 5,703
  • 2
  • 16
  • 35
Eric Kolotyluk
  • 1,958
  • 2
  • 21
  • 30