Is async/await more suitable for user interfaces?
Is async/await more suitable for user interfaces?
1. We know that no JVM frame contains a raw pointer to anywhere else in the stack.
2. We know which stack frames are JVM frames, and which are native frames.
3. We know that there are unlikely to be native frames in the portion of the stack between the point where we yield control and the point we want to yield to.
4. We can change the standard library to check if we are in our new lightweight thread or not and act appropriately.
Knowing these we can avoid function coloring and push the complex management of green threads into the standard library, and reuse existing task scheduler code to manage these virtual threads, and we can work to make thread local variables etc work seamlessly with this new concurrency abstraction.
This puts us in a very different design space. We can move a portion of the stack to the heap when a thread is unmounted, and we can change things about the GC and other internal systems to make them aware of this new thing that can be in the heap.
This type of approach would be much harder in a language like swift that tends to coexist with C and other languages that use raw pointers or a non-moving GC, so I think the question is not which is the better approach but which is the better approach within your language ecosystem.
Of course, there are technical differences that make certain design choices easier or harder in different languages. As aardvark179 mentions above, Java doesn't have pointers into the stack, and intermediate FFI frames are extremely rare (also due to the platform's established and large ecosystem).
(I'm the technical lead for OpenJDK's Project Loom)
Narking on grandpa's compiler is just what some kids do.
Now, I actually still believe Java is doing the right thing (hence the name). And the right thing if successful is more right, thus the name. And you make a good point in that Java is already popular and can probably afford to take a really long time to bring a better async story to its table.
But lately I've been wondering if Loom can fail? It seems to still somewhat be experimental, is there any chance it wouldn't ship? That after all the hard work, it doesn't get merged into OpenJDK? Maybe it turns out to be more hassle, break too much user code, be too difficult to bring back to Graal, or fail to be natively compiled by Substrate, etc.
Hopefully not, and I'm sure you're more aware then anyone of the risks here. And I trust you and the team working on Loom. But sometimes seeing what's happening and being done in C# and other languages, I do stop and wonder if worse is better, but when Loom does arrive (if ever?), I'll probably be happy that this was the route Java took.
co-routine (Java Loom's one, Go one, Scheme one, etc) requires to be able to serialize and deserialize a part of the stack (move the stack on the the heap and vice versa), so it's hard/impossible to implement with non managed runtimes which like in C or objective C.
In C, you can declare an address/pointer to an address on stack (using &), but with a co-routine mechanism, the addresses of parts of the stack are not constant.
If you have a managed runtime, you rewrite those kind of pointers when you copy parts of the stack back and forth.
The benefits that Go (and potentially Loom) provide are with the scheduler. When Go code calls into other Go code, it's fast because preemption points can be inserted by the compiler. The goroutine is parked when it is blocked (on I/O or some foreign function), and in the slow path this logic is executed on a new kernel thread.
Although the Go approach involves more overhead in the slow path, wrangling blocking code to work with the scheduler has cross-cutting implications for library design. I.e, I don't have to worry that a library I import that does file I/O will pin my goroutine to a blocked thread by using a blocking syscall.
It seems like it would be much harder or impossible in languages that work to hide these details. How do you go from implicit blocking to explicit native thread usage?
At least Loom seems to be keeping native threads so Java code can simply fall back to that, I guess.
This of course requires a scheduler that needs to handle hierarchical control of execution rather than having an unformed mass of tasks, but nurseries provide other benefits as well.
Also, here's an interesting use of async/await: software hyperthreading: https://isocpp.org/blog/2019/09/cppcon-2018-nano-coroutines-...
But won't you run into issues where you don't want to call certain code from that UI thread? Isn't that "code coloring"?
In your example, for Swing applications you should use invokeLater(Runnable r) anyway, and it doesn't matter what implements the interface.
How can you set a shader and then draw a mesh each in two virtual threads without possibly interweaving their execution, for example?
... assuming any other tasks/coroutines running on the UI thread are being cooperative and not doing bad things like waiting on synchronous functions or otherwise hogging the UI thread too much. Done right, it's a big performance and maintainability win over multithreading, but when done poorly, it can result in large variance in latency/responsiveness of any individual task.