코루틴 공식 가이드 자세히 읽기 — Part 2 — Dive 2

Myungpyo Shim
4 min readMay 31, 2019

--

withTimeout 은 코루틴의 수행시간을 어떻게 제한하고 있을까?

Korean [English]

(목차로 돌아가기)

공식 가이드에서는 코루틴의 실행 시간을 제한할 수 있게 만들어주는 withTimeout { } 중단 함수에 대한 설명을 하기에 앞서서 이러한 기능을 묘사해 볼 수 있는 샘플 코드를 소개하고 있습니다.

위 샘플코드를 다시 한번 간단히 살펴보겠습니다.

우선 500ms 간격으로 “I’m sleeping” 을 출력하는 코루틴A가 있습니다. 이 코루틴A의 실행으로 얻게 된 Job 객체를 로컬 변수로 유지하고, 또다른 코루틴B를 실행합니다. 이 코루틴B는 앞서 실행한 코루틴A의 Job을 참조하고 있다가 1.3초 후에 코루틴A가 아직도 실행중이라면 취소시키는 동작을 수행합니다.

그렇다면 withTimeout { } 중단함수의 실제 구현도 이와 같을까요?
결론부터 이야기하자면 코루틴 프레임워크 내부 구현도 이와 유사합니다.

withTimeout 중단 함수는 다음과 같이 생겼습니다.

파라미터로 제한할 시간(timeMillis)과 실행할 중단함수블록(block)을 전달 받습니다. 중요한 setupTimeout 부분입니다. 이 함수는 TimeoutCoroutine 과 중단함수블록을 파라미터로 받습니다.

우선 TimeoutCoroutine 을 먼저 살펴 보겠습니다.

여기서 눈여겨 보아야 할 부분은 TimeoutCoroutine 이 Runnable 을 구현하고 있으며, 구현부인 run() { } 블록을 보면 TimeoutCancellationException 을 발생시키며 현재 코루틴을 취소시키는 부분입니다.

이제 다시 setupTimeout 함수로 돌아가서 이 함수가 TimeoutCoroutine 과 실행할 코드가 있는 중단함수블록을 어떻게 사용하는지 살펴보겠습니다.

cont 는 현재 실행중인 코루틴의 Continuation 객체이며, 여기서 현재 코루틴 컨텍스트도 참조합니다.

우선 8번 라인의 context.delay.invokeOnTimeout 함수를 살펴봅시다.

이 함수는 주어진 시간의 지연 후에 주어진 Runnable 을 실행해주는 함수이며 지연시간에 도달하기 전에 이 작업을 취소할 수 있도록 DisposableHandle 을 반환해 줍니다. 앞서 보았던 것처럼 TimeoutCoroutine 은 Runnable 인터페이스를 구현하고 있으며 이 함수의 두번째 인자로 전달되고 있습니다.

disposeOnCompletion 함수는 현재 코루틴이 완료될 경우 전달된 DisposableHandle 을 dispose 합니다. 지금의 경우에는 코루틴이 정상적으로 완료되면 TimoutCoroutine 이 동작하지 않도록 취소하는 역할을 수행하게 됩니다.

지금까지의 살펴본 withTimeout 함수의 동작 방식을 그림으로 나타내보면 다음과 같습니다.

withTimeout 함수로 전달 된 500ms 의 타임아웃 값은 현재 코루틴 컨텍스트의 DelayExecutor에 의해 스케쥴링 되고, 지정된 시간에 도달하면TimeoutCoroutine (Runnable) 을 실행하여 코루틴을 취소하게 됩니다. 이렇게 타임아웃 설정을 완료한 후 수행하고자 하는 중단함수블록을 자식 코루틴을 생성하여 실행합니다. 자식 코루틴으로 실행되던 코드 블록은 타임아웃 시간이 되면 부모 코루틴이 취소됨으로써 함께 취소 됩니다.

처음에 공식가이드에서 샘플로 보여준 코드에서는 이러한 타임아웃 구현의 핵심을 보여주기 위해서 간단하게 구현되어 있습니다. withTimeout의 실제 구현과 비교하여 누락된 기능 하나를 추가해 보겠습니다.

샘플 코드에서 누락된 기능은 수행하고자 하는 작업이 타임아웃 내에 완료되면 타임아웃을 위해 생성한 코루틴은 취소되어야 하는데 계속 유지되고 있는 부분입니다. 이 부분을 구현해보면 다음과 같이 수정할 수 있습니다.

수행하고자 하는 작업이 타임아웃 코루틴보다 먼저끝나도록 2회만 수행하도록 조정하였습니다. 추가된 코드는 21 번째 라인에서 수행하고자 하는 job이 완료되면 타임아웃 코루틴을 취소하도록 리스너 설정을 한 부분입니다.

추가로, 코루틴에는 withTimeoutOrNull 함수도 준비되어 있습니다. 이 함수는 코루틴 타임아웃에 도달하면 예외를 발생시키는 대신 null 을 반환합니다.

끝.

(목차로 돌아가기)

--

--