4. 코루틴의 실제 구현
코틀린 중단 함수는
continuation-passing style(CPS)로 구현되어 있다.- 여러 중단 함수 구현 방식 중 CPS를 선택하였다.
Continuation객체는 상태를 나타내는 숫자와 로컬 데이터를 가지고 있다.Continuation은 함수 인자로 전달된다.중단 함수는 상태(state) 를 가지는 state machine 으로 볼 수 있다.
중단 함수의
Continuation객체가 이 함수를 부르는 다른 함수의Continuation객체를 decorate(장식) 한다.모든
Continuation객체는 resume 하거나 함수가 완료 될때 사용되는 콜 스택으로 사용된다.
| |
아주 간단한 중단 함수 알아보기
- 함수가 시작되는 지점은 함수의 시작점과 중단 이후 재개 시점(컨티뉴에이션이 resume을 호출할 때) 두 곳이다.
- 현재 상태를 저장에는 label 이라는 field 를 사용한다. (처음에는 0으로 설정)
- 이후에는 중단되기 전에 다음 상태로 설정되어 재개 될 시점을 알 수 있게 해준다.
- delay에 의해 중단된 경우
COROUTINE_SUSPENDED를 반환 - myFunction 을 호출한 함수부터 시작해 콜 스택에 있는 모든 함수도 똑 같다.
- 따라서, 중단이 일어나면 콜 스택에 있는 모든 함수가 종료된다
- 중단된 코루틴을 실행하던 스레드를 (다른 종류의 코루틴을 포함해) 실행 가능한 코드가 사용할 수 있게 된다.
| |
| |
상태를 가진 함수
- 함수가 중단된 후에 다시 사용할 지역 변수나 파라미터와 같은 상태를 가지고 있다면, 함수의 컨티뉴에이션 객체에 상태를 저장해야 한다.
- 함수 내에서 사용되던 값들은 중단되기 직전에 저장되고, 이후 함수가 재개될 때 복구된다.
값을 받아 재개되는 함수
- 중단 함수로 부터 값을 받아야 하는 경우 좀 더 복잡해짐
- token은 상태 0과 1에서 사용
- userId는 상태 1과 2에서 사욛
- Result 타입인 result는 함수가 어떻게 재개되었는지 나타냄
- 함수가 값으로 재개 되면 결과는
Result.Success(result) - 함수가 예외로 재개 되면 결과는
Result.Failure(exception)
- 함수가 값으로 재개 되면 결과는
| |
콜 스택
함수 a가 함수 b를 호출하면 가상 머신은 a의 상태와 b가 끝나면 실행이 될 지점을 콜 스택(call stack)이라는 자료 구조 에 저장한다.
코루틴을 중단하면 스레드를 반환해 콜 스택에 있는 정보가 사라질 것이다.
- 따라서 코루틴을 재개 할 때, 콜 스택을 사용할 수 없다.
- 대신
Continuation객체가 콜 스택의 역할을 대신한다.
Continuation객체는 중단 되었을때 상태(label)와 함수의 지역 변수와 파라미터(필드), 중단 함수를 호출한 함수가 재개될 위치 정보를 가지고 있다.- 하나의
Continuation객체는 다른 하나를 참조하는 식으로 동작한다. Continuation객체가 재개 될 때, 각Continuation객체는 자신이 담당하는 함수를 호출하고,- 그 함수의 실행이 끝나면, 자신을 호출한 함수의
Continuation객체을 재개 합니다.complete.resumeWith(res)
- 이 과정은 스택의 끝에 다다를 때까지 반복된다.
- 하나의
중단된 함수가 재개했을 때
Continuation객체로 부터 상태를 복원하고, 얻은 결과를 사용하거나 예외를 던진다.컨티뉴에이션 객체에 상태가 저장된며, 중단을 처리하기 위한 과정이 있어야 한다.
중단된 함수가 재개했을 때 컨티뉴에이션 객체로부터 상태를 복원하고, 얻은 결과를 사용하거나 예외를 던져야 한다.
예외를 던질 때도 비슷하다, 처리되지 못한 예외가
resumeWith에서 잡히면Result.failure(e)로 래핑되며, 예외를 던진 함수를 호출한 함수는 포장된 결과를 받는다.함수 c에서 중단된 상황을 예로 들면
실행이 재개되면 c의 컨티뉴에이션 객체는 c 함수를 먼저 재개
함수가 완료되면 c 컨티뉴에이션 객체는 b 함수를 호출하는 b 컨티뉴에이션 객체를 재개
b 함수가 완료되면 b 컨티뉴에이션은 a 컨티뉴에이션을 재개하고 a 함수가 호출된다.



중단 함수의 성능
- 함수를 상태로 나누는 것은 숫자를 비교하는 것만큼 쉬운 일이며 실행점이 변하는 비용 또한 거의 들지 않음
- 컨티뉴에이션 객체에 상태를 저장하는 것 또한 간단하다
- 지역 변수를 복사하지 않고 새로운 변수가 메모리 내 특정 값을 가리키게 한다.
Reference
- https://kotlinlang.org/docs/coroutines-guide.html
- 코틀린 코루틴 Kotlin Coroutines: Deep Dive (Marcin Moskała, 인사이트)