Skip to content

Instantly share code, notes, and snippets.

@ryszardmakuch
Last active April 12, 2022 19:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ryszardmakuch/eeada8ec807a658f3d2ca0f544b627c3 to your computer and use it in GitHub Desktop.
Save ryszardmakuch/eeada8ec807a658f3d2ca0f544b627c3 to your computer and use it in GitHub Desktop.
Coroutines
# Coroutines:
How do Kotlin coroutines work internally?
- https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761,
- https://medium.com/androiddevelopers/coroutines-on-android-part-i-getting-the-background-3e0e54d20bb,
- https://medium.com/androiddevelopers/coroutines-on-android-part-ii-getting-started-3bff117176dd,
- https://medium.com/androiddevelopers/coroutines-on-android-part-iii-real-work-2ba8a2ec2f45,
- https://medium.com/@patson.luk/learning-kotlin-coroutines-as-a-java-dev-part-i-a04029b6214b,
- https://medium.com/@patson.luk/learning-kotlin-coroutines-as-a-java-dev-part-ii-dfe0d468b65e,
- https://medium.com/@esocogmbh/coroutines-in-pure-java-65661a379c85.
## Coroutines suspending state machines:
- https://labs.pedrofelix.org/guides/kotlin/coroutines/coroutines-and-state-machines,
- https://medium.com/google-developer-experts/coroutines-suspending-state-machines-36b189f8aa60.
## Structured concurrency:
- https://www.youtube.com/watch?v=Mj5P47F6nJg,
- https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/,
- https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/,
- https://libdill.org/structured-concurrency.html.
# Loom
- https://wiki.openjdk.java.net/display/loom/Main,
- https://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part1.html,
- https://cr.openjdk.java.net/~rpressler/loom/loom/sol1_part2.html,
- https://renato.athaydes.com/posts/taking-loom-for-a-spin.html,
- https://blog.usejournal.com/project-loom-virtual-threads-in-java-371dccc88b0f.
@ryszardmakuch
Copy link
Author

ryszardmakuch commented Jun 28, 2020

package pl.rmakuch

import kotlinx.coroutines.*
import java.util.concurrent.TimeUnit
import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            parent()
        }
    }
    print("Completed in: $time ms.")
}

suspend fun parent() {
    println("${Thread.currentThread().name} Hello from parent suspend function!")
    delay(1000)
    println("${Thread.currentThread().name} Going to call child suspend function!")
    child()
}

suspend fun child() {
    println("${Thread.currentThread().name} I'm a child and I'm going to do some IO blocking work!")
    val result = withContext(Dispatchers.IO) {
        val firstString = async { blockingIOTask() }
        val secondString = async { blockingIOTask() }
        firstString.await() + secondString.await()
    }
    println("${Thread.currentThread().name} And here's expected result: ${result}.")
}

fun blockingIOTask(): String {
    TimeUnit.MILLISECONDS.sleep(2000)
    return "${Thread.currentThread().name} Read data from disk";
}

main Hello from parent suspend function!
main Going to call child suspend function!
main I'm a child and I'm going to do some IO blocking work!
main And here's expected result: DefaultDispatcher-worker-3 Read data from diskDefaultDispatcher-worker-2 Read data from disk.
Completed in: 3228 ms.

@ryszardmakuch
Copy link
Author

// CoroutinesKt$main$time$1$1.java
package pl.rmakuch;

import kotlin.Metadata;
import kotlin.ResultKt;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlin.coroutines.jvm.internal.DebugMetadata;
import kotlin.coroutines.jvm.internal.SuspendLambda;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.CoroutineScope;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@DebugMetadata(
   f = "Coroutines.kt",
   l = {10},
   i = {0},
   s = {"L$0"},
   n = {"$this$runBlocking"},
   m = "invokeSuspend",
   c = "pl.rmakuch.CoroutinesKt$main$time$1$1"
)
@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 3,
   d1 = {"\u0000\u000e\n\u0000\n\u0002\u0010\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\u0010\u0000\u001a\u00020\u0001*\u00020\u0002H\u008a@¢\u0006\u0004\b\u0003\u0010\u0004"},
   d2 = {"<anonymous>", "", "Lkotlinx/coroutines/CoroutineScope;", "invoke", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"}
)
final class CoroutinesKt$main$time$1$1 extends SuspendLambda implements Function2 {
   private CoroutineScope p$;
   Object L$0;
   int label;

   @Nullable
   public final Object invokeSuspend(@NotNull Object $result) {
      Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      CoroutineScope $this$runBlocking;
      switch(this.label) {
      case 0:
         ResultKt.throwOnFailure($result);
         $this$runBlocking = this.p$;
         this.L$0 = $this$runBlocking;
         this.label = 1;
         if (CoroutinesKt.parent(this) == var3) {
            return var3;
         }
         break;
      case 1:
         $this$runBlocking = (CoroutineScope)this.L$0;
         ResultKt.throwOnFailure($result);
         break;
      default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }

      return Unit.INSTANCE;
   }

   CoroutinesKt$main$time$1$1(Continuation var1) {
      super(2, var1);
   }

   @NotNull
   public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
      Intrinsics.checkParameterIsNotNull(completion, "completion");
      CoroutinesKt$main$time$1$1 var3 = new CoroutinesKt$main$time$1$1(completion);
      var3.p$ = (CoroutineScope)value;
      return var3;
   }

   public final Object invoke(Object var1, Object var2) {
      return ((CoroutinesKt$main$time$1$1)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
   }
}
// CoroutinesKt.java
package pl.rmakuch;

import java.util.concurrent.TimeUnit;
import kotlin.Metadata;
import kotlin.ResultKt;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlin.coroutines.jvm.internal.ContinuationImpl;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.CoroutineStart;
import kotlinx.coroutines.Deferred;
import kotlinx.coroutines.DelayKt;
import kotlinx.coroutines.Dispatchers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 1, 16},
   bv = {1, 0, 3},
   k = 2,
   d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0004\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a\u0011\u0010\u0002\u001a\u00020\u0003H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0004\u001a\u0006\u0010\u0005\u001a\u00020\u0003\u001a\u0011\u0010\u0006\u001a\u00020\u0003H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0004\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u0007"},
   d2 = {"blockingIOTask", "", "child", "", "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "main", "parent", "kotlin-coroutines"}
)
public final class CoroutinesKt {
   public static final void main() {
      int $i$f$measureTimeMillis = false;
      long start$iv = System.currentTimeMillis();
      int var5 = false;
      BuildersKt.runBlocking$default((CoroutineContext)null, (Function2)(new CoroutinesKt$main$time$1$1((Continuation)null)), 1, (Object)null);
      long time = System.currentTimeMillis() - start$iv;
      String var6 = "Completed in: " + time + " ms.";
      boolean var7 = false;
      System.out.print(var6);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }

   @Nullable
   public static final Object parent(@NotNull Continuation $completion) {
      Object $continuation;
      label27: {
         if ($completion instanceof <undefinedtype>) {
            $continuation = (<undefinedtype>)$completion;
            if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
               ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
               break label27;
            }
         }

         $continuation = new ContinuationImpl($completion) {
            // $FF: synthetic field
            Object result;
            int label;

            @Nullable
            public final Object invokeSuspend(@NotNull Object $result) {
               this.result = $result;
               this.label |= Integer.MIN_VALUE;
               return CoroutinesKt.parent(this);
            }
         };
      }

      Object $result = ((<undefinedtype>)$continuation).result;
      Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      StringBuilder var10000;
      Thread var10001;
      String var1;
      boolean var2;
      switch(((<undefinedtype>)$continuation).label) {
      case 0:
         ResultKt.throwOnFailure($result);
         var10000 = new StringBuilder();
         var10001 = Thread.currentThread();
         Intrinsics.checkExpressionValueIsNotNull(var10001, "Thread.currentThread()");
         var1 = var10000.append(var10001.getName()).append(" Hello from parent suspend function!").toString();
         var2 = false;
         System.out.println(var1);
         ((<undefinedtype>)$continuation).label = 1;
         if (DelayKt.delay(1000L, (Continuation)$continuation) == var5) {
            return var5;
         }
         break;
      case 1:
         ResultKt.throwOnFailure($result);
         break;
      case 2:
         ResultKt.throwOnFailure($result);
         return Unit.INSTANCE;
      default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }

      var10000 = new StringBuilder();
      var10001 = Thread.currentThread();
      Intrinsics.checkExpressionValueIsNotNull(var10001, "Thread.currentThread()");
      var1 = var10000.append(var10001.getName()).append(" Going to call child suspend function!").toString();
      var2 = false;
      System.out.println(var1);
      ((<undefinedtype>)$continuation).label = 2;
      if (child((Continuation)$continuation) == var5) {
         return var5;
      } else {
         return Unit.INSTANCE;
      }
   }

   @Nullable
   public static final Object child(@NotNull Continuation $completion) {
      Object $continuation;
      label20: {
         if ($completion instanceof <undefinedtype>) {
            $continuation = (<undefinedtype>)$completion;
            if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
               ((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
               break label20;
            }
         }

         $continuation = new ContinuationImpl($completion) {
            // $FF: synthetic field
            Object result;
            int label;

            @Nullable
            public final Object invokeSuspend(@NotNull Object $result) {
               this.result = $result;
               this.label |= Integer.MIN_VALUE;
               return CoroutinesKt.child(this);
            }
         };
      }

      Object $result = ((<undefinedtype>)$continuation).result;
      Object var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      String result;
      StringBuilder var8;
      Object var10000;
      Thread var10001;
      switch(((<undefinedtype>)$continuation).label) {
      case 0:
         ResultKt.throwOnFailure($result);
         var8 = new StringBuilder();
         var10001 = Thread.currentThread();
         Intrinsics.checkExpressionValueIsNotNull(var10001, "Thread.currentThread()");
         result = var8.append(var10001.getName()).append(" I'm a child and I'm going to do some IO blocking work!").toString();
         boolean var2 = false;
         System.out.println(result);
         CoroutineContext var9 = (CoroutineContext)Dispatchers.getIO();
         Function2 var10 = (Function2)(new Function2((Continuation)null) {
            private CoroutineScope p$;
            Object L$0;
            Object L$1;
            Object L$2;
            Object L$3;
            int label;

            @Nullable
            public final Object invokeSuspend(@NotNull Object $result) {
               Object var10000;
               StringBuilder var5;
               Object var6;
               label17: {
                  Object var7 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                  CoroutineScope $this$withContext;
                  Deferred firstString;
                  Deferred secondString;
                  switch(this.label) {
                  case 0:
                     ResultKt.throwOnFailure($result);
                     $this$withContext = this.p$;
                     firstString = BuildersKt.async$default($this$withContext, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
                        private CoroutineScope p$;
                        int label;

                        @Nullable
                        public final Object invokeSuspend(@NotNull Object $result) {
                           Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                           switch(this.label) {
                           case 0:
                              ResultKt.throwOnFailure($result);
                              CoroutineScope $this$async = this.p$;
                              return CoroutinesKt.blockingIOTask();
                           default:
                              throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                           }
                        }

                        @NotNull
                        public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                           Intrinsics.checkParameterIsNotNull(completion, "completion");
                           Function2 var3 = new <anonymous constructor>(completion);
                           var3.p$ = (CoroutineScope)value;
                           return var3;
                        }

                        public final Object invoke(Object var1, Object var2) {
                           return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                        }
                     }), 3, (Object)null);
                     secondString = BuildersKt.async$default($this$withContext, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
                        private CoroutineScope p$;
                        int label;

                        @Nullable
                        public final Object invokeSuspend(@NotNull Object $result) {
                           Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                           switch(this.label) {
                           case 0:
                              ResultKt.throwOnFailure($result);
                              CoroutineScope $this$async = this.p$;
                              return CoroutinesKt.blockingIOTask();
                           default:
                              throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                           }
                        }

                        @NotNull
                        public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                           Intrinsics.checkParameterIsNotNull(completion, "completion");
                           Function2 var3 = new <anonymous constructor>(completion);
                           var3.p$ = (CoroutineScope)value;
                           return var3;
                        }

                        public final Object invoke(Object var1, Object var2) {
                           return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                        }
                     }), 3, (Object)null);
                     var5 = new StringBuilder();
                     this.L$0 = $this$withContext;
                     this.L$1 = firstString;
                     this.L$2 = secondString;
                     this.L$3 = var5;
                     this.label = 1;
                     var10000 = firstString.await(this);
                     if (var10000 == var7) {
                        return var7;
                     }
                     break;
                  case 1:
                     var5 = (StringBuilder)this.L$3;
                     secondString = (Deferred)this.L$2;
                     firstString = (Deferred)this.L$1;
                     $this$withContext = (CoroutineScope)this.L$0;
                     ResultKt.throwOnFailure($result);
                     var10000 = $result;
                     break;
                  case 2:
                     var5 = (StringBuilder)this.L$3;
                     secondString = (Deferred)this.L$2;
                     firstString = (Deferred)this.L$1;
                     $this$withContext = (CoroutineScope)this.L$0;
                     ResultKt.throwOnFailure($result);
                     var10000 = $result;
                     break label17;
                  default:
                     throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                  }

                  var6 = var10000;
                  var5 = var5.append((String)var6);
                  this.L$0 = $this$withContext;
                  this.L$1 = firstString;
                  this.L$2 = secondString;
                  this.L$3 = var5;
                  this.label = 2;
                  var10000 = secondString.await(this);
                  if (var10000 == var7) {
                     return var7;
                  }
               }

               var6 = var10000;
               return var5.append((String)var6).toString();
            }

            @NotNull
            public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
               Intrinsics.checkParameterIsNotNull(completion, "completion");
               Function2 var3 = new <anonymous constructor>(completion);
               var3.p$ = (CoroutineScope)value;
               return var3;
            }

            public final Object invoke(Object var1, Object var2) {
               return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
            }
         });
         ((<undefinedtype>)$continuation).label = 1;
         var10000 = BuildersKt.withContext(var9, var10, (Continuation)$continuation);
         if (var10000 == var6) {
            return var6;
         }
         break;
      case 1:
         ResultKt.throwOnFailure($result);
         var10000 = $result;
         break;
      default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }

      result = (String)var10000;
      var8 = new StringBuilder();
      var10001 = Thread.currentThread();
      Intrinsics.checkExpressionValueIsNotNull(var10001, "Thread.currentThread()");
      String var7 = var8.append(var10001.getName()).append(" And here's expected result: ").append(result).append('.').toString();
      boolean var3 = false;
      System.out.println(var7);
      return Unit.INSTANCE;
   }

   @NotNull
   public static final String blockingIOTask() {
      TimeUnit.MILLISECONDS.sleep(2000L);
      StringBuilder var10000 = new StringBuilder();
      Thread var10001 = Thread.currentThread();
      Intrinsics.checkExpressionValueIsNotNull(var10001, "Thread.currentThread()");
      return var10000.append(var10001.getName()).append(" Read data from disk").toString();
   }
}

@ryszardmakuch
Copy link
Author

ryszardmakuch commented Jun 28, 2020

@jisungbin
Copy link

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment