지연 값 초기화

지연 값 초기화 (Lazy Vals Initialization)

Scala 3는 SIP-20에서 개선된 지연 값(lazy val) 초기화 제안 중 버전 6을 구현해요.

출처: Scala 3 Reference

본문

동기 (Motivation)

새로 제안된 지연 값 초기화 메커니즘은 지연 값 초기화 블록이 실행되는 동안 자원(락 등)을 획득하는 일을 없애서, 교착 상태(deadlock) 가능성을 줄이는 게 목표예요. 새 지연 값 초기화 방식이 제거하는 구체적인 교착 상태 시나리오는 SIP-20 문서에 정리되어 있어요.

구현 (Implementation)

다음 형태의 지연 필드가 있다고 해볼게요.

class Foo {
  lazy val bar = <RHS>
}

Scala 3 컴파일러는 이 코드에 해당하는 다음 코드를 생성해요.

class Foo {
  import scala.runtime.LazyVals
  var value_0: Int = _
  var bitmap: Long = 0L
  val bitmap_offset: Long = LazyVals.getOffset(classOf[LazyCell], "bitmap")

  def bar(): Int = {
    while (true) {
      val flag = LazyVals.get(this, bitmap_offset)
      val state = LazyVals.STATE(flag, <field-id>)

      if (state == <state-3>) {
        return value_0
      } else if (state == <state-0>) {
        if (LazyVals.CAS(this, bitmap_offset, flag, <state-1>, <field-id>)) {
          try {
            val result = <RHS>
            value_0 = result
            LazyVals.setFlag(this, bitmap_offset, <state-3>, <field-id>)
            return result
          }
          catch {
            case ex =>
              LazyVals.setFlag(this, bitmap_offset, <state-0>, <field-id>)
              throw ex
          }
        }
      } else /* if (state == <state-1> || state == <state-2>) */ {
        LazyVals.wait4Notification(this, bitmap_offset, flag, <field-id>)
      }
    }
  }
}

지연 값의 상태 <state-i>0, 1, 2, 3 네 개의 값으로 표현돼요. 상태 0은 아직 초기화되지 않은 지연 값을 뜻하고, 상태 1은 어떤 스레드가 지금 초기화하고 있는 지연 값을 뜻해요. 상태 2는 지연 값에 동시 독자(reader)가 있다는 뜻이고, 상태 3은 초기화가 끝난 지연 값을 나타내요. <field-id>는 지연 값의 id인데, 클래스에 정의된 휘발성(volatile) 지연 값의 개수에 따라 커져요.

재귀 지연 값에 대한 참고

이상적으로는 재귀 지연 값(recursive lazy val)을 오류로 표시해야 해요. 지금의 재귀 지연 값 동작은 정의되지 않았어요(초기화가 교착 상태로 이어질 수 있어요).

참고 자료 (Reference)

더 알아보기 (Learn more)