I am a playframework user, the async/await is usefull in many long io wait scene
I found this article https://kotlinlang.org/docs/reference/comparison-to-scala.html say will support Yield operator
Are there any plans?
We haven’t done any post-1.0 planning yet, so we can’t give any time frame, but async/await support is on the list of features that we’re going to look into after 1.0 is out.
The way Play Framework implements this is using a library called JavaFlow. I’ve recently (like, yesterday) started experimenting with how to use this best in Kotlin. I suspect async/await can be integrated with Kotlin apps without changes to the official language or compiler … although of course, having it integrated with the compiler (perhaps via a plugin mechanism) would be tremendously useful.
I successfully used JavaFlow back in 2008, but the project never made it to 1.0. In 2015, Quasar (GitHub - puniverse/quasar: Fibers, Channels and Actors for the JVM) seems like a much better bet, at least for languages that it officially supports (Java, Kotlin, Clojure).
I wonder though, once you have full delimited continuations on the JVM (using JavaFlow, Kilim or Quasar), why would you still want async/await? I consider the latter a much more limited facility, typically chosen as a compromise for when full delimited continuations are out of reach.
It would be great to see “native” support for async/await in Kotlin, without the complexity that comes with an implementation of full delimited continuations (in particular on today’s JVM), great IDE (debugger) support, etc. With Java based projects drowning in the complexity caused by async programming models, and async/await not coming to Java any time soon, this could be another strong reason for adopting Kotlin.
I’m also a big fan of Quasar, but have witnessed some anxiety in adopting it, due to the magic involved (complex bytecode rewriting) and potential consequences thereof (on performance, debugging, etc.). I haven’t had a chance to use Quasar in a team/production setting, so can’t share any real-world experiences.
Also please note that Quasar (a great async/fiber library) works very well with Kotlin: http://blog.paralleluniverse.co/2015/06/04/quasar-kotlin/
I tried Quasar first. Perhaps I was just missing a key bit of knowledge, but I had a few problems with it:
- I didn’t want to use the built in actors framework. I just wanted a continuation that I could pass objects into and out of. It seemed like the only way to do that with fibers was to use channels.
- You have to specify the java agent on the command line. Making this work with Maven/Gradle was awkward, the approaches suggested seemed to all end up affecting the developer experience. I didn’t want people who checked out my code and opened it in IntelliJ to have extra work to do. JavaFlow, in contrast, has a classloader that does the bytecode rewriting at load time.
- The JavaFlow docs made it clear to me how I’d write a continuation that could be serialised whilst blocking. When I tried to figure this out in Quasar I found you can’t easily combine this with blocking.
JavaFlow has the serious problem that it’s not maintained. You need a random version that’s uploaded by the play framework team to use it on Java 8. But the library is small and simple, so I might fork it and apply the necessary patches myself.
I’ve finished experimenting with it now. The resulting code looks quite good. The big caveat being that mixing classloaders means you have to be careful to not try and directly access the continuation object, but rather, only do so via interfaces.
From my experience/understanding of C#, async/await are features of WPF/Silverlight (and whatever superseded these) as a way to get around limitations in the threading model. Basically, the insistence to do all UI work on the UI thread.
What I’m trying to say is that async/await are a feature of the framework. (The framework’s event loop is what schedules the continuations.) I’m not sure how a language can support async/await in an agnostic way.
As an update, I worked with Ron Pressler a bit and he helped me figure out how to use Quasar in the slightly unusual way I wanted. Javaflow was good enough to get me past the prototyping stage in my project, but Quasar is much better supported.
async/await support at the language level can be useful, in theory, to avoid the need for on-the-fly bytecode rewriting. It’s obviously more direct to have the compiler emit the necessary code in the right shape the first time rather than rewrite it later using an agent or AOT bytecode wrangler.
The main concern I have is that I suspect async/await in Kotlin would end up being not very useful, even in combination with a framwork, because you will inevitably want to suspend a continuation across Java stack frames … not only Kotlin. And as javac won’t be supporting async/await any time soon, that means that bytecode rewriting on the fly is likely inevitable anyway.
Ron said that Java 9 may have a new API that makes the use of continuation frameworks a lot easier, as you wouldn’t have to annotate methods as @Suspendable any more. If we do get that, then I’m not sure how async/await in the language would make things any easier.
But that’s exactly the difference between async/await (as seen in C#, Dart, Scala, ES7, etc.) and delimited continuations (JavaFlow, Quasar, etc.): async/await does not support suspending across stack frames, hence is much simpler to implement. Ron explains the difference on Reddit and Hacker News.
To summarize my point:
- async/await: simple, but limited
- delimited continuations: complex/costly (esp. on today’s JVMs), but powerful
- async/await implemented using delimited continuations: as limited as #1, as complex/costly as #2
I don’t see why a user would want #3.
I am not 100% sure of what you mean regarding async/await vs “delimited continuations.” Allow me to take a guess.
In C#, the compiler transforms something like:
val result: String = await foo();
... rest of code ...
into a callback to a lambda:
Task<String> bar = foo();
bar.beginAwait {
val result: String = bar.endAwait();
... rest of code ...
}
By contrast, Quasar creates something closer to:
// Backing fields
var continuationAddress: int;
var savedVariables: Map<String,Object>;
// We enter the method and check if we need to continue
if (continuationAddress != 0) {
restore (savedVariables)
goto label[continuationAddress];
}
... some code ...
continuationAddress = 42
savedVariables = save()
foo() // This async function will throw a SuspendExecution exception
label[42]: // We'll jump to this label when the scheduler decides to continue
foo() // We return into foo since it may not have run all of its code either
... rest of code ... // Hopefully we'll get here, but foo() might suspend again
In Quasar, every method in the callstack needs the continuation machinery.
However, C# also needs machinery at every point in the callstack. Every async method (and every method that calls an async method) needs to return a Task
object which takes a callback. The compiler will create these.
To me, both methods look very similar, it’s just that C# utilizes existing lambda machinery (which does the heavy lifting of saving local variables and an instruction pointer). I’d like to know why you think Quasar is more powerful.
Despite being implemented differently, the thing that both have in common is needing to annotate every method in the callstack (similar to checked exceptions in Java) to signal to the programmer that bad, multithreaded stuff may happen and also that this method has been modified by the compiler. C# uses the “async
” annotation on methods, and Quasar mostly gets away with declaring an uncatchable checked exception.
For starters, Kotlin would need this checked annotation. Maybe that’s what Ron was referring to in regard to Java 9.
Kotlin might want to stop here, and leave implementation to other libraries which will do bytecode surgery.
The problem is that you need to have a scheduler at the top doing the actual continuing, and if Kotlin provides its own scheduler, noone is going to use it. The scheduler has to be endorsed by the framework both at the highest and lowest levels. The scheduler has to be controlled by the event loop, as well as react in response to OS events like incoming packets or completed I/O operations.
You can’t simply enjoy async/await in a vacuum. Let’s say you launch your own scheduler in a framework that doesn’t use it. You fire a disk I/O and suspend your method, and your custom I/O routine will resume you. Great. But what work is your scheduler going to launch while this method is suspended?
The links I posted are a good starting point for understanding the (significant) difference.
Nope. What he’s talking about is a new efficient stack walking API. In fact, one has already been added to Java 9 but it doesn’t expose all the data Quasar needs (locals/opstacks). Apparently Oracle will try and support it as a HotSpot specific extension, i.e. that bit of the API won’t be a standard part of Java, at least to start with.
The idea is simple. JVM Instrumentation API allows you to rewrite the bytecodes of methods that are already in use, including methods that are currently live on the stack. The new bytecode is used next time the method is entered. The rewriter allows both the stack to be captured, and control flow to resume at the right place when the fiber is re-entered. Because currently there’s no way to know which methods might be on the stack when suspension occurs, you have to manually annotate all of them. Boo hiss.
But if the JVM provided an API to grab the current contents of the stack, then this requirement would go away, because at the suspend point the stack could be walked and any un-instrumented methods that are discovered could then be rewritten and replaced on the fly. Next time they’re executed they’d have to resume-point logic in them.
Thus, the need to annotate or have propagating tags like async/await would go away. You’d just be able to suspend any stack including stacks that were never intended to be used in fibers. Seems a lot more convenient and powerful than async/await.
I see what you’re saying.
But is it wise to get rid of explicit async/await keywords? In .NET, at least, async/await was bolted onto a framework that was single-threaded. This co-operative multi-threading was safer and easier than true multi-threading. However, thought had to be given to every await
statement. Hence, it was good to be explicit.
I wonder, in the JVM world, are fibers only used to reduce the OS overhead of multi-threading, or do some frameworks use cooperative multi-threading for its own sake?
Fibers aren’t used much. One is overhead of threading (really … the memory used by the stacks, as at least the Linux kernel scheduler is very efficient).
Another reason to use them, the reason I’m exploring, is that you can serialise them. Thus, you can checkpoint and resume them after a crash.
Instead of forking JavaFlow let me invite you to try out my own fork
There are a lot of original design issues fixed (like omission of continuable method “markers” that leads to aggressive stack capture before any method call), Java8 support is added (for ex., lambdas are supported). And as an icing on the cake there is an async/await implementation atop of this (GitHub - vsilaev/tascalate-async-await: Async / Await asynchronous programming model for Java version 1.8 though 17; similar to the functionality available in C# 5. The implementation is based on continuations for Java (see my other projects).) – currently implemented as Java Agent, but compile-time Maven plugin will be available shortly. I would really appreciate if you try it out and left a feedback – never tested my code with other JVM languages.
Plus, there is another rework of JavaFlow that looks very promising:
It’s almost a complete departure from JavaFlow from user API stand point, but the core idea is very similar.
The point is FWICS whether async/await in Kotlin will be backed by fibers instead of threads. Without fibers it is merely the same as working with BlockingQueues where you can run out of threads, because the machine has run out of resources to create a new thread. It is not clear to me whether the Kotlin developers are going with fibers now or not. Maybe some reply to clarify would be helpful :-).
Regards, Oliver
async/await in Kotlin will be backed by anything you want. The compiler’s task is to transform a function into a state machine allowing to suspend its execution at points of async calls and resume it after the async call completes. The specific mechanism for running the function is entirely the responsibility of the library, and you can build libraries that execute async functions via thread pools, fibers, remote process calls or anything else that you might want.
great job~ i also need this~