Ron Pressler leads Project Loom at OpenJDK and writes on fibers, virtual threads, and continuations for the JVM.
HN links his Loom documents when discussing lightweight threads, async/await, and tail calls on the JVM. Commenters mention him as the Quasar library's author.
I gotta say, as a programmer "of a certain age", a lot of recent advances have an everything-old-is-new-again feel to me. When I first heard of "threads" in the 90s, the term specifically referred to a mechanism for intra-process concurrency that was "lightweight", i.e. it didn't have the heavy overhead of forking a new OS process. IIRC they weren't even preemptively multitasked. Then preemptive "OS threads" became the new hotness and everything using cooperative threads was old and busted. Now a generation has passed and we have "virtual threads" or things like asyncio to solve the problems created by the thing that solved the problems of the thing before.
A similar thing has happened at the OS level with support for multiple programs. Computers started out running one program at a time. Then "multitasking" was invented, and along with it all kinds of things to make it safer and easier like users and permissions and protected memory, and it was great, many different users could run different programs on the same machine. Then we moved from running statically linked binary executables to dynamic linking and interpreted languages that load libraries at run time. Managing environments for different programs on one machine got hard. VMs and hypervisors let us segment a machine into virtual machines where the environments were easy to control. But VMs were a heavyweight solution for managing environments, so along came containers, which are kind of virtual virtual…
From http://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html:
"Whereas the OS can support up to a few thousand active threads, the Java runtime can support millions of virtual threads. Every unit of concurrency in the application domain can be represented by its own thread, making programming concurrent applications easier. Forget about thread-pools, just spawn a new thread, one per task. You’ve already spawned a new virtual thread to handle an incoming HTTP request, but now, in the course of handling the request, you want to simultaneously query a database and issue outgoing requests to three other services? No problem — spawn more threads. You need to wait for something to happen without wasting precious resources? Forget about callbacks or reactive stream chaining — just block. Write straightforward, boring code. All the benefits threads give us — control flow, exception context, debugging flow, profiling organization — are preserved by virtual threads; only the runtime cost in footprint and performance is gone. There is no loss in flexibility compared to asynchronous programming because, as we’ll see, we have not ceded fine-grained control over scheduling."
This seems so obvious in hindsight as the "right way". I wonder why we went down that whole async/await craze with so many languages?
The best and most careful description of all these concepts in a single source is the Project Loom proposal: http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html
Other than this paper, http://www.inf.puc-rio.br/~roberto/docs/corosblp.pdf, which revived interest in coroutines, I don't have links handy for authoritative sources in the academic literature regarding nomenclature. "Stackful" and "stackless" are relatively recent descriptions and not too long ago they could have meant the complete opposite--Stackless Python (http://www.stackless.com/spcpaper.htm) implements what would now be called stackful coroutines.[1] But I think it's fair to say that today there's a relatively fixed nomenclature about the concepts. But people still create unnecessary confusion by conflating implementation details (often coincidental or accidental) with the abstract concepts.
[1] The Stackless Python website talks about microthreads and tasklets, but the original paper begins the discussion with continuations, generators, and coroutines, and builds up from there.
>> single kernel thread
> This is pretty much how JavaScript operates
> javascript has exactly 1 thread
No disagreement here.
I understand what you are saying. Would be great if you had tried to understand what I said.
Maybe my explanation is lacking. Let me quote pron.
> Again, threads — at least in this context — [...] refer only to the abstraction allowing programmers to write sequences of code that can run and pause.
https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html#header-n67
An kernel thread running code line-by-line is a "thread"; callbacks that are sequenced (and have the dreaded pyramid indentation hell) forms a "thread"; when an async function is called there is also a "thread".
In JavaScript the latter two kinds of "threads" are run by one single kernel thread, i.e. N:1 threading.
> libc is pretty shallow. The majority are either leaf functions like strlen(), or <5 levels from a leaf/before execution disappears into a system call.)
I'd wager that the JIT in OP's picture will transform that huge call stack into something <5 levels from CPU instructions/syscalls.
What remains though, is the huge context carried around such a stack; and I'm quite excited at what the Loom project [1] could yield to tackle that.
Such huge call stacks are a good sign to me. I don't want to implement all the corner cases of all the RFC I rely on; and it is great that library implementors can organise their code well. In the end the JIT will adapt my program to the currently used corner case, and prune everything else.
[1] http://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html
For the JVM it's worth noting that Project Loom[1], while so far focused mainly on concurrency features like virtual threads and continuations also includes a proposal to implement better tail calls in the JVM:
> it is also the goal of this project to add an even lighter-weight construct that will allow unwinding the stack to some point and then invoke a method with given arguments (basically, a generalization of efficient tail-calls). We will call that feature unwind-and-invoke, or UAI. It is not the goal of this project to add an automatic tail-call optimization to the JVM.
Since this paragraph is explicit about the feature not being "automatic", it'll be interesting to see what form this takes.
[1] https://cr.openjdk.java.net/~rpressler/loom/Loom-Proposal.html