티스토리 뷰
코틀린 내부 동작 방식 이해하기
- 코틀린 코루틴 책 4장의 주제를 정리
코루틴이 쓰인 코루틴 코드
fun main() {
CoroutineScope(Dispatchers.Default).launch {
val token = fetchToken()
println(token)
}.join()
}
suspend fetchToken(){
delay(1000)
return "1234"
}
자바 디컴파일로 변환 했을 때의 코드를 중요한 부분만 적은 수도코드
(코틀린, 자바 짬뽕인 수도코드)
fun main(completion:Continuation) {
var var10000 = BuildersKt.launch$default(/** ... */, Function2() {
int label = 0
fun invoke(var1, var2) {
// ...
return create(var1,var2).invokeSuspend(Unit.INSTANCE)
}
fun create(var1, var2){
// ...
return var2 as Fuction2
}
})
fun invokeSupsend(result: Object) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000
switch(label) {
0: {
label = 1
var10000 = fetchToken(this)
if(var10000 == var3) // suspend라면 리턴
return var3
break;
}
1: {
var10000 = result
break;
}
}
val res = var10000
println(res)
return Unit.INSTANCE
}
}
fun fetchToken(var3:Continuation) {
Object continuation;
// continuation을 초기화 하기 위한 처리가 있음
if(continuation이 초기화가 필요하면) {
continuation = Continuation(var3){
Object result
Object label
fun invokeSuspend(Object result) {
this.result = result
label = 값 조작
return fetchToken(this)
}
}
}
val label = continuation.label
Object result = continuation.result
val var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch(label) {
0: {
continuation.label = 1
if(delay(1000, continuation) == IntrinsicsKt.getCOROUTINE_SUSPENDED()){
return var3
}
break;
}
1: {
break;
}
}
return "1234"
}
순서
코루틴실행
- main안의 Function2의 invoke -> create -> invokeSuspsend
- fetchToken
- delay를 만나면 그대로 리턴됨
suspend되어서 함수들의 리턴이 발생
- 기존 함수 콜스택과 똑같음
- fetchToken 리턴 -> main의 invokeSuspend 리턴 -> invoke 리턴 -> main함수 리턴
🔆[중요] delay 후 코루틴 resume🔆
- fetchToken의 continuation의 invokeSusppend 실행
- fetchToken 실행
- fetchToken은 label1단계라서 1234를 리턴
- continuation의 invokeSupspend의 리턴은 1234
- main함수의 invokeSupsend 실행
- invokeSupsend의 아큐먼트는 fetchToken의 continuation의 invokeSuspend의 리턴값. 즉. 1234
- res변수의 값은 1234가 됨
추가적으로, 디컴파일 코드 원문
// Main3Kt.java
package com.heenu.yoonnote.screen;
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.coroutines.jvm.internal.RunSuspendKt;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.CoroutineScopeKt;
import kotlinx.coroutines.CoroutineStart;
import kotlinx.coroutines.DelayKt;
import kotlinx.coroutines.Dispatchers;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Metadata(
mv = {1, 8, 0},
k = 2,
d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\u001a\u0011\u0010\u0000\u001a\u00020\u0001H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u001a\u0011\u0010\u0003\u001a\u00020\u0004H\u0086@ø\u0001\u0000¢\u0006\u0002\u0010\u0002\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u0005"},
d2 = {"fetchToken", "", "(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;", "main", "", "app_debug"}
)
public final class Main3Kt {
@Nullable
public static final Object main(@NotNull Continuation $completion) {
Object var10000 = BuildersKt.launch$default(CoroutineScopeKt.CoroutineScope((CoroutineContext)Dispatchers.getDefault()), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch (this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
var10000 = Main3Kt.fetchToken(this);
if (var10000 == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String res = (String)var10000;
System.out.println(res);
return Unit.INSTANCE;
}
@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function2 var3 = new <anonymous constructor>(completion);
return var3;
}
public final Object invoke(Object var1, Object var2) {
return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
}
}), 3, (Object)null).join($completion);
return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}
// $FF: synthetic method
public static void main(String[] var0) {
RunSuspendKt.runSuspend(new Main3Kt$$$main(var0));
}
@Nullable
public static final Object fetchToken(@NotNull Continuation var0) {
Object $continuation;
label20: {
if (var0 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var0;
if ((((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
break label20;
}
}
$continuation = new ContinuationImpl(var0) {
// $FF: synthetic field
Object result;
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return Main3Kt.fetchToken(this);
}
};
}
Object $result = ((<undefinedtype>)$continuation).result;
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (((<undefinedtype>)$continuation).label) {
case 0:
ResultKt.throwOnFailure($result);
((<undefinedtype>)$continuation).label = 1;
if (DelayKt.delay(1000L, (Continuation)$continuation) == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
return "1234";
}
}
// Main3Kt$$$main.java
package com.heenu.yoonnote.screen;
import kotlin.Metadata;
import kotlin.coroutines.Continuation;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Lambda;
// $FF: synthetic class
@Metadata(
mv = {1, 8, 0},
k = 3
)
final class Main3Kt$$$main extends Lambda implements Function1 {
// $FF: synthetic field
private final String[] args;
// $FF: synthetic method
Main3Kt$$$main(String[] var1) {
super(1);
this.args = var1;
}
// $FF: synthetic method
public final Object invoke(Object var1) {
return Main3Kt.main((Continuation)var1);
}
}
비트연산 코드
- 보다보면 비트연산이 3개가 나옴
(((<undefinedtype>)$continuation).label & Integer.MIN_VALUE)
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
this.label |= Integer.MIN_VALUE;
1번째 코드
(((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0
- Integer.MIN_VALUE : 1000 0000 0000 0000 0000 0000 0000 0000
- & 연산자는 두 비트 모두 1일 때만 결과가 1이 되고, 그렇지 않으면 0.
- (((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) != 0
- label의 최상위 비트를 체크
- 코루틴이 중단 될때 최상위 비트를 1로 설정
- 즉, 결과가 1이라면 이미 중단된 상태라는 뜻
- 즉, (((<undefinedtype>)$continuation).label & Integer.MIN_VALUE) = 1 이라서 저 조건문은 true
2번째 코드
((<undefinedtype>)$continuation).label -= Integer.MIN_VALUE;
- label의 최상위 비트를 0으로 만듬
3번째 코드
this.label |= Integer.MIN_VALUE;
- label의 최상위 비트를 1로 만듬
[정리]
- 코루틴이 중단되었는지 파악하기 위해서 비트연산자를 사용
- continution.label이 1이면 중단된 상태
'개발 > 코틀린' 카테고리의 다른 글
변성 - 공변, 무공변, 반공변 (0) | 2020.11.22 |
---|
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 코루틴
- 자바
- 코틀린코루틴스터디
- 변성
- 반공변
- 안드로이드
- databinding
- dynamiclink
- #윈도우
- 프로그래머스
- #tensorflow
- 코틀린
- #EC2
- 키보드
- Android
- Hilt
- stateflow
- 힐트
- 코루틴딥다이브
- 공변
- 다이나믹링크
- 코루틴내부동작
- 코틀린코루틴
- Kotlin
- TF-Slim
- #우분투
- 코루틴동시성프로그래밍
- c++
- 코틀린으로배우는함수형프로그래밍
- 무공변
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
글 보관함