- Kotlin Coroutine은 일시중단을 구현하기 위해 ContinuosPassing style을 적용하였음
 
CPS스타일로 변환된 Suspend 함수
1
2
3
4
5
6
7
8
  | suspend fun printUser(token: String) {
  println("Before")
  val userId = getUserId(token) // suspending
  println("Got userId: $userId")
  val userName = getUserName(userId, token) // suspending
  println(User(userId, userName))
  println("After")
}
  | 
 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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  | fun printUser(
    token: String,
    continuation: Continuation<*>
        ): Any {
    val continuation = continuation as? PrintUserContinuation
    ?: PrintUserContinuation(
        continuation as Continuation<Unit>,
        token
    )
    var result: Result<Any>? = continuation.result
    var userId: String? = continuation.userId
    val userName: String
    if (continuation.label == 0) {
        println("Before")
        continuation.label = 1
        val res = getUserId(token, continuation)
        if (res == COROUTINE_SUSPENDED) {
            return COROUTINE_SUSPENDED
        }
        result = Result.success(res)
    }
    if (continuation.label == 1) {
        userId = result!!.getOrThrow() as String
        println("Got userId: $userId")
        continuation.label = 2
        continuation.userId = userId
        val res = getUserName(userId, continuation)
        if (res == COROUTINE_SUSPENDED) {
            return COROUTINE_SUSPENDED
        }
        result = Result.success(res)
    }
    if (continuation.label == 2) {
        userName = result!!.getOrThrow() as String
        println(User(userId as String, userName))
        println("After")
        return Unit
    }
    error("Impossible")
}
class PrintUserContinuation(
    val completion: Continuation<Unit>,
    val token: String
) : Continuation<String> {
    override val context: CoroutineContext
    get() = completion.context
    var label = 0
    var result: Result<Any>? = null
    var userId: String? = null
    override fun resumeWith(result: Result<String>) {
        this.result = result
        val res = try {
            val r = printUser(token, this)
            if (r == COROUTINE_SUSPENDED) return
            Result.success(r as Unit)
        } catch (e: Throwable) {
            Result.failure(e)
        }
        completion.resumeWith(res)
    }
}
  | 
- 함수의 오퍼레이션이 변경됨
- 마지막 인자로 continuation이 생김
- continuation은 현재 코루틴의 상태를 가지고 있는 상태머신임
 - 항상 function의 마지막 인자로 추가됨
 
 - return 타입이 Any로 변경됨
- Any?로 바뀌는 이유는 실제 리턴타입 뿐만아니라, suspend된다면 COROUTINE_SUSPENDED을 반환해야하기 때문
 
 
 
추후 kotlin에 유니온 타입이 추가된다면 User?|COROUTINE_SUSPENDED가 될 수 있음
5번 라인, Continuation이 해당 함수의 Continuation인지 확인하고, 아니라면 생성
- resume될떄는 해당 함수의 Continuation이므로, 처음 실행될때만 생성함
 
11,12,13번 라인, 지역변수들을 선언하고, 값을 대입
- 11번 result변수는 직전에 호출한 suspend 함수의 결과 가짐
 - 12번 userId는 여러 단계(1,2)에 걸쳐서 필요하므로 Continuation에 저장됨
 - 13번 userName은 한번의 단계에서만 사용하므로 result로 가져올수 있어 따로 저장되지 않음
 
Continuation은 label을 가짐
- label로 현재 어디까지 코드가 진행되었는지 파악하고, 다음 실행때 어디부터 시작할지 결정함
 
suspend된다면, COROUTINE_SUSPENDED을 리턴 후 중단이 끝난 후 다시시작함
- io작업이 발생한다면 작업을 끝내지 못하므로 우선 COROUTINE_SUSPENDED을 리턴함
 
suspend이후 resume된다면, PrintUserContinuation의 resumeWith가 호출됨
- 22번 라인과 56번라인이 동일 기능을 함
 - 앞서 말한다로 직전에 호출한 suspend 함수의 결과를 result변수에 넣음
 
콜스택 마지막에 있는 함수의 continuation이 supend후 resume되고, 작업을 다 끝마치면, 바로 상위 함수의 continuation의 resume을 호출함

https://kt.academy/article/cc-under-the-hood