How much of a leaky abstraction coroutines are?


#1

Kotlin coroutines implementation looks like CPS(stackless) state machine generator with api for continuation scheduling factory. Looking at “Guide to kotlinx.coroutines by example” this makes it really powerful implementation in some ways superior to JS and C# async/await analogs.

One troubling thing is traditionally CPS is considered very hard to reason and almost humanly impossible to debug.
Coroutine context aware logging is not nearly enough.

What tools/best practices/patterns are planned to mitigate this problem? With kotlin native and js in mind will they be usable for different targets considering big differences in underlying platforms nature?

Bugs aside performance and monitoring tools are also needed. It wood be nice to be have tools to inspect not only state but have support in profilers and production monitoring tools. Without explicit support coroutines will be shown as bunch of weird generated objects in memory, instrumentation and heavy reflection based tools may break coroutines contracts and show almost complete nonsense as result.

So, any plans on tooling and such?


#2

We plan to support coroutines across the board on all Kotlin backends. They are already available in both JVM and JS (including ES5, that does not have a native coroutine support) and will be available on Native at some future point. The implementations differ, of course, but APIs that Kotlin provides are the same.

We plat a lot of work with respect to coroutine tooling and debugging in the timeframe of Kotlin 1.2. Instead of describing our currents plans, let us use this thread to gather community feedback of what coroutine-related debugging and tooling features you think would be most helpful in working with coroutine-based code.


#3

I would not rely on community doing expertise on this matters. There are very small amount of people having actual experience in that advanced domain and they are the only ones capable of providing valuable feedback and guidance.

Existing solutions in C# and JS are obviously limited and originally targeted at solving very specific set of problems. My guess would be “better then they have” for tooling. As for patterns, practices and such they does not look that applicable to kotlin analogs due to different magnitude of possibilities.

Anyways despite most obvious requirement being list of live coroutines and channels in debugger with pause I would suggest investigating Erlang/Go/Akka other popular communities for their experience and I would say traditions in dealing with that execution model.


#4

As for supporting across the board please don’t claim it until battle tested.
Reason is execution specifics are really different across platforms, which makes writing cross platform CPS code really hard.
For example take this typical run 100000 coroutine sample. With sleeps in coroutine body it will essentially be transformed to 100000 invocations of setTimeout(0, this.coroutineContext) on JS target which will be really bad both in node and browsers.


#5

[quote=“Konstantin-Khvan”]
Kotlin coroutines implementation looks like CPS(stackless) state machine generator with api for continuation scheduling factory. Looking at “Guide to kotlinx.coroutines by example” this makes it really powerful implementation in some ways superior to JS and C# async/await analogs.[/quote]
I guess you are referring to CSP which means communicating sequential processes. As you don’t seem to know much about it I can recommend to read the book about it by the creator of CSP, Toni Hoare, which you can get from here as a PDF.

No, Go which is fully based on CSP is a big success. It is currently on Tiobe at position 14. There are many Go developers nowadays. Apparently they picked up CSP very quickly and really like it.

No, reading concurrent code based on channels is a lot easier than trying to follow asynchronous calls. It is also a lot easier to find the reason for a race condition or deadlock and to fix the issue than with asynchronous programming. The is the very point of CSP. Robert Pike is saying in Origins of Go Concurrency (from about position 29:00):

“The thing that is hard to understand until you’ve tried it is that the whole business about finding deadlocks and things like this doesn’t come up very much. If you use mutexes and locks and shared memory it comes up about all the time. And that’s because it is just too low level. If you program like this (using channels) deadlocks don’t happen very much. And if they do it is very clear why, because you have got this high-level state of your program ‘expressing this guy is trying to send here and he can’t, because he is here’. It is very very clear as opposed to being down on the mutex and shared memory level where you don’t know who owns what and why. So I’m not saying it is not a problem, but it is not harder than any other class of bugs you would have.”

In Go you cannot look at a goroutine in the debugger at all. Goroutines are completely locked away from the user. This is on purpose, because it is supposed to be transparent and the user is prevented this way from creating a mess. The success of Go and the enthusiasm of Go developers shows that the point you are mentioning is not an issue. Not every debugger can handle some ten thousand or hundred thousand green threads (aka coroutines or goroutines). This is something you could have brought forward. But you didn’t due to lack of understanding.

I suggest that YOU investigate other popular communities and investigate CSP a bit more carefully. This way many of your harsh and inappropriate comments could have been avoided. CSP is so easy to study by looking at Go where things are very well explained and documented. But apparently you didn’t have a look into all the material provided there.


#6

continuation passing style.

There is no direct support for this in JS/LLVM/JVM. What you will get in existing debug tools for them is tree of defferred state machine objects on heap somehow referencing outside state and pushing continuations to factory provided executors. Tomcat/android/browser framework and library threading and state will still be in process, it will be the mess in case of any non trivial usage outside of basic async/await. The only current way provided to understand whats actually happening is Thread.setName called on continuation resume. This is just not sufficient.

GO is self contained environment with optimizing compiler, complex implementation of threading and memory management, kotlin is language for JVM/Android/JS/LLVM. The topic question is not about golang channels/CSP at all, but support tools and guides on using kotlin “suspend” continuation generators in practice. This includes not only channels but async/await, actors. yields and such.

My main point is this kind of tools are not just nice to have, they are actual necessity even for non distributed cases: