Loom has a worse API, is hidden from the user (what's preemptible and what will cause OS thread starvation? who knows!), and effectively ends up infecting the whole JVM with its complexity.
Go shares most of those downsides, but as a greenfield project (..heh) there's at least the expectation that all libraries play nice with their scheduler. They also try to hide FFI starvation by effectively running each FFI call in a dedicated OS thread[0].
There's also gevent/greenlet, which tries to bolt "colourless" M:N scheduling onto Python. It hasn't exactly taken the Python world by storm either, despite being far older than their native asyncio support.
Overall, I'd consider all three implementations of the approach to be dead on arrival.
[0]: See https://stackoverflow.com/a/28356944, it's a bit obfuscated but the overall effect is the same
Interesting point regarding scheduler and the interruptibility of FFI calls. Just thinking about it I don't see how this can be addressed. So definitely interested in learning here: what is the remedy to your criticism here beyond "document behavior"?
No, the JDK had builtin green-threads behind a command line flag around Java 1.1 on Solaris.
Also, Java has the luxury that almost no code makes use of FFI, so their impact is minimal.
Does it? Preemptability is pretty robustly solved in Go. While it's sometimes less efficient than I'd like, it's a far cry from the initial draft of virtual threads in java where a misplaced "synchronized" could sneakily starve entire executors. Even with CGo pinning (which, as you indicate, is rare due to the bulk of pure-Go libraries) I'm having trouble thinking of reasonable situations where full starvation or deadlock occurs rather than (admittedly unpleasant!) latency and queueing.
That said, Loom is quite new and deliberately iterative in approach, and the Java core folks are quite skilled, so I do expect to see substantial improvements in behavior and issue-detection tooling here over the coming years.
> I'd consider all three implementations of the approach to be dead on arrival
I'd argue that eventlet/greenlet failed for different reasons than the concurrency model or starvation risk (the monkeypatching approach just can't sustainably work in an ecosystem as big and native-code-dependent as Python's), hence Python's falling back to traditional async/await cooperative concurrency.
The Rust folks are struggling with the same choices here as well, further constrained by their requirements of a GC-less runtime with near-zero-overhead C FFI, which precludes them from using Go's approach. There was some great discussion of that here: https://news.ycombinator.com/item?id=39242962
If by green threads you mean virtual threads in JDK 21 could you please elaborate why they are a mistake? I'm not a Java developer but from what I see new concurrency model allows to write much more efficient network software - OS threads are expensive (in terms of RAM/CPU) and to handle many thousands of network connections (C10k problem) you have to either throw a lot of hardware or use NIO (New/Non-blocking I/O) which is hard to code.
One of reasons why Go become popular - gorutines look like normal threads (relatively easy to code) but more efficient.