i started learning kotlin Coroutines. But I am facing a situation like this.
fun main(){
println("${Thread.currentThread().name} thread started")
GlobalScope.launch {
println("${Thread.currentThread().name} thread started")
Thread.sleep(1000)
println("${Thread.currentThread().name} thread finished")
}
GlobalScope.launch {
println("${Thread.currentThread().name} thread started")
delay(1000)
println("${Thread.currentThread().name} thread finished")
}
GlobalScope.launch {
println("${Thread.currentThread().name} thread started")
delay(1000)
println("${Thread.currentThread().name} thread finished")
}
runBlocking {
delay(1000)
}
println("${Thread.currentThread().name} thread finished")
}
the output of this code:
main thread started
DefaultDispatcher-worker-1 thread started
DefaultDispatcher-worker-2 thread started
DefaultDispatcher-worker-3 thread started
DefaultDispatcher-worker-1 thread finished
DefaultDispatcher-worker-1 thread finished
DefaultDispatcher-worker-2 thread finished
main thread finished
Why "DefaultDispatcher-worker-1 thread finished" is running 2 times.
I guess it's just the threads' names are changing, but I'm wondering why ?
Coroutines provide no guarantee that the whole coroutine will run on the same thread for its whole duration. Thread management is handled by the dispatchers you use. Dispatchers.Default uses a thread pool and can swap threads each time it resumes from suspension, as it does after each delay() call. This is intended behavior, as it can reduce the total number of spawned Thread objects.
Related
Problem
I am using kotlinx.coroutines.future.FutureKt#await to await for an async code. But when any exception is thrown from this async code, the Exception doesn't contain full stack call. E.g.:
fun main() {
try {
myFun1Blocking()
} catch (e: Throwable) {
e.printStackTrace(System.out)
}
}
fun myFun1Blocking() {
runBlocking {
myFun2Suspend()
}
}
suspend fun myFun2Suspend() {
runAsync().await()
}
fun runAsync(): CompletableFuture<Void> {
return CompletableFuture.runAsync {
Thread.sleep(2000)
throw Exception()
}
}
This results in the following output:
java.lang.Exception
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:34)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
The stack trace only has the runAsync method part.
Solution #1
While trying to workaround this problem, I first thought of catching it just outside of await:
suspend fun <T> CompletionStage<T>.awaitWithException(): T {
try {
return await()
} catch (e: Exception) {
throw Exception(e)
}
}
java.lang.Exception: java.lang.Exception
at TestKotlinKt.awaitWithException(TestKotlin.kt:36)
at TestKotlinKt$awaitWithException$1.invokeSuspend(TestKotlin.kt)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at TestKotlinKt.myFun1Blocking(TestKotlin.kt:23)
at TestKotlinKt.main(TestKotlin.kt:16)
at TestKotlinKt.main(TestKotlin.kt)
Caused by: java.lang.Exception
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:43)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Better, but I still lost the myFun2Suspend call in the stack.
Solution #2
Then I tried saving the stacktrace just before await():
suspend fun <T> CompletionStage<T>.awaitWithException(printStream: PrintStream): T {
val throwable = Throwable("Await Exception")
try {
return await()
} catch (e: Exception) {
throwable.printStackTrace(printStream)
throw e
}
}
java.lang.Throwable: Await Exception
at TestKotlinKt.awaitWithException(TestKotlin.kt:43)
at TestKotlinKt.myFun2Suspend(TestKotlin.kt:31)
at TestKotlinKt$myFun1Blocking$1.invokeSuspend(TestKotlin.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:274)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at TestKotlinKt.myFun1Blocking(TestKotlin.kt:25)
at TestKotlinKt.main(TestKotlin.kt:18)
at TestKotlinKt.main(TestKotlin.kt)
AsyncException
at TestKotlinKt.runAsync$lambda-0(TestKotlin.kt:55)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Now I see everything.
All in all, my solution seems to be very hacky. Is there something better?
Referring to Kotlin's documentation about debugging both of your solutions have officially supported counterparts:
Set the system property kotlinx.coroutines.debug to on to enable debug mode. This enables stacktrace recovery which is a more comprehensive version of solution #1.
Use the Kotlin debug agent to enable creation stacktraces which is the official version of solution #2. Do be aware that this is a very expensive feature because it will need to dump stack traces each time a coroutine is created.
In theory kotlin debug mode should be enough since the exception has to unwind through the 'stack' of coroutines. It just isn't the prettiest solution.
I faced the same problem. The Kotlin coroutine debug library didn't help me in any way.
Therefore, after studying the implementation of coroutines, I wrote my own solution based on bytecode generation and MethodHandle API. It supports JVM 1.8 and Android API 25 or higher.
I called it Stacktrace-decoroutinator.
The reason why the stacktrace is lost is that when the coroutine wakes up, only the last method of its call stack is called.
My library replaces the coroutine awakening implementation. It generates classes at runtime with names that match the entire coroutine call stack.
These classes don't do anything except call each other in the coroutine call stack sequence.
Thus, if the coroutine throws an exception, they mimic the real call stack of the coroutine during the creation of the exception stacktrace.
I am using OkHttp to make a synchronous http request. To avoid blocking the main thread, I wrapped the blocking network call in a suspend function and withContext(Dispatchers.IO)
suspend fun run(): String {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://publicobject.com/helloworld.txt")
.build()
return withContext(Dispatchers.IO) {
val response = client.newCall(request).execute()
return#withContext "Request completed successfully"
}
}
Android Studio gives me a warning that execute() is an "Inappropriate blocking method call". My understanding is that execute() will block during the http request taking up a thread in Dispatchers.IO for the duration of the request, which is not ideal. To avoid this issue, I can use the asynchronous version of the request wrapped in suspendCoroutine
suspend fun runAsync(): String = suspendCoroutine { continuation ->
val client = OkHttpClient()
val request = Request.Builder()
.url("http://publicobject.com/helloworld.txt")
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
continuation.resumeWithException(e)
}
override fun onResponse(call: Call, response: Response) {
response.use {
if (!response.isSuccessful) throw IOException("Unexpected code $response")
continuation.resume("Request completed successfully")
}
}
})
}
This avoids the warning, but I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
I do not understand how it is functionally different than the synchronous version above. I am assuming that the async version of the http call uses a thread to wait on the request. Is this assumption correct? If not, how does the async function wait for the callback to return?
It's not correct, the point of the async approach is that there are no threads being blocked while the call takes place. Your second approach achieves just that -- when the coroutine suspends, it doesn't live on any thread at all. Depending on the implementation details of OkHttp, it may or may not be the case that some internal thread is blocked because it relies on blocking IO. Ideally it should be implemented in terms of Java NIO (more typically, through the Netty library) and rely on the low-level non-blocking primitive called the Selector.
The correct approach is the way you did it with implementing suspension, and kotlin-coroutines-okhttp already provides exactly that implementation in its Call.await() function.
I'm working on a library that is a file which get some high order functions
My file is like:
import okhttp3.*
private val client by lazy { OkHttpClient() }
fun fn() {
client.newCall(request(url)).enqueue(callback)
do stuff ...
}
...
When I call some fn(), it continues running on background blocking the program to exit, even there is no more instructions to execute. I suspect it happens because of .enqueue(callback) which is asynchronous.
If you upgrade to the latest OkHttp 4.7.2, then it won't block your program as the threads are now daemon threads.
Clean shutdown is documented in the API docs for OkHttpClient.
https://square.github.io/okhttp/4.x/okhttp/okhttp3/-ok-http-client/
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
When I am running a main thread as in a public static void main method, the thread terminates when the method completes. I don't do anything explicit to shutdown the thread.
Why then in case of ExecutorService. we have a shutdown() method? Why cannot we just let the thread(s) managed by ExecutorService run to completion?
The invocation of shutdown method just means that no new tasks will be accepted by the executor. You can give a timeout to the running tasks(to run to completion) submitted to the executor.
executor.shutdown();
try {
//blocks until all tasks have finished or timeout occurs
executor.awaitTermination(TimeUnit.MICROSECONDS , Long.MAX_VALUE);
} catch (InterruptedException e) {
//Take some action at least Thread.currentThread().interrupt();
}
What I found out was if you throw a FaultException from a new worker thread, it doesnt percolate up to the client but just crashes WCF.
Any solutions???
example:
var thread = new Thread(new ThreadStart(
delegate
{
new Killbot().KillAllHumans(); // Throws a FaultException
}));
The simplest way would be to wrap the call in a try-catch block and log the exception:
var thread = new Thread(new ThreadStart(
delegate
{
try
{
new Killbot().KillAllHumans(); // Throws a FaultException
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}
}));
If you want to handle the exception in your main thread you would have to use BeginInvoke and EndInvoke in combination with an AsyncCallback.
Personally I would not bother with background threads in a WCF service. A service is effectively a "background worker" anyway. All you need to do is ensure that any blocking calls you make inside the service don't affect other clients. You can do this by changing the concurrency mode:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
class MyServiceClass : IMyServiceContract {
public void KillAll() {
new Killbot().KillAllHumans(); // Throws a FaultException
}
}
When that is set, WCF will call your service methods on multiple threads with no attempt to synchronise them. As long as you write your code with this in mind, you can do all the blocking calls you want.