인자 생성 제약 조건(Argument generation constraints)

인자 생성 제약 조건(Argument generation constraints)

동시성 데이터 구조를 테스트할 때 Lincheck는 작업을 여러 스레드에 무작위로 배치하고 무작위 인자로 호출하여 동시성 시나리오 집합을 생성합니다. 인자 값의 범위를 제한하면 동시성 버그를 찾을 확률을 높일 수 있어요.

출처: Argument generation constraints

본문

동시성 데이터 구조를 테스트할 때 Lincheck는 작업을 여러 스레드에 무작위로 배치하고 무작위 인자로 호출하여 동시성 시나리오 집합을 생성합니다.

인자 값의 범위를 제한하면 동시성 버그를 찾을 확률을 높일 수 있어요. 예를 들어 해시 맵에서 동시 작업이 같은 키에 접근할 가능성을 높이려면 가능한 키 값의 범위를 제한하면 됩니다. 이렇게 하면 Lincheck가 경쟁 조건(race condition)과 같은 동시성 버그를 더 효율적으로 드러낼 수 있습니다.

Lincheck에서 생성되는 인자 값의 범위를 제한하는 방법은 다음과 같아요:

  1. @Param 어노테이션으로 인자 생성기(generator)를 선언합니다:

    @Param(name = "key", gen = IntGen::class, conf = "1:2")
    class MultiMapTest {
    }
    
    • name – 인자 생성기의 이름입니다.
    • gen – 생성기의 타입입니다.
    • conf – 생성기의 설정 문자열입니다. 여기서는 Lincheck가 1부터 2까지의 정수 값을 생성합니다.
  2. 작업의 파라미터에 @Param을 붙여 제약 조건을 적용합니다:

        @Operation
        fun add(@Param(name = "key") key: Int, value: Int) = map.add(key, value)
    
        @Operation
        fun get(@Param(name = "key") key: Int) = map.get(key)
    

제약 조건이 적용되면 Lincheck는 지정된 범위 안의 값만 사용해 시나리오를 생성합니다:

| ---------------------------------- |
|    Thread 1     |     Thread 2     |
| ---------------------------------- |
| add(2, 0): void | add(2, -1): void |
| ---------------------------------- |
| get(2): [-1]    |                  |
| ---------------------------------- |

생성기 타입

Lincheck는 다음과 같은 인자 생성기 타입을 제공합니다:

| 생성기 | 설정 템플릿 | 설명 | | IntGen | "min:max" | minmax 사이의 Int 값을 포함하여 생성합니다. 설정 문자열이 비어 있으면 Int.MIN_VALUE부터 Int.MAX_VALUE까지 전체 정수 범위를 사용합니다. 예: "1:3" -> [1, 2, 3] | | StringGen | "maxWordLength:alphabet" | 주어진 alphabet에서 maxWordLength 길이까지의 무작위 문자열 값을 생성합니다. 기본 alphabet[a-zA-Z\d _]이고 기본 maxWordLength15입니다. "" 설정 문자열이면 기본값을 사용합니다. | | EnumGen | "Enum.Const1,Enum.Const2,..." | 지정된 열거형(enum) 값 목록에서 무작위 값을 생성합니다. | | BooleanGen | "" | truefalse 값을 생성합니다. 특정 설정 문자열이 필요하지 않습니다. 예: "" -> [true, false] | | DoubleGen | "start:step:end" | start부터 end까지 step만큼 증가하며 Double 값을 생성합니다. 기본 step 값은 (end - start)/100입니다. "start:end" 형식으로 지정하면 기본 step을 사용합니다. 설정 문자열이 비어 있으면 step = 0.1Int.MIN_VALUE부터 Int.MAX_VALUE까지 값을 생성합니다. 예: "0.0:0.1:1.0" -> [0.0, 0.1, 0.2, ..., 0.9, 1.0] | | FloatGen | "start:step:end" | DoubleGen과 동일하지만 값을 Float로 변환합니다. 예: "0.0:0.1:1.0" -> [0.0, 0.1, 0.2, ..., 0.9, 1.0] | | LongGen | "min:max" | IntGen과 동일하지만 값을 Long으로 변환합니다. 예: "1:3" -> [1, 2, 3] | | ShortGen | "min:max" | minmax 사이의 Short 값을 포함하여 생성합니다. 설정 문자열이 비어 있으면 -32768부터 32767까지 전체 short 정수 범위를 사용합니다. 예: "1:3" -> [1, 2, 3] | | ByteGen | "min:max" | minmax 사이의 Byte 값을 포함하여 생성합니다. 설정 문자열이 비어 있으면 -128부터 127까지 전체 byte 범위를 사용합니다. 예: "1:3" -> [1, 2, 3] | | ThreadIdGen | "" | 현재 스레드의 ID 번호를 반환합니다. 특정 설정 문자열이 필요하지 않습니다. 예: "" -> [1, 2] |

더 알아보기