Skip to content

[AI Refactoring] VideoRecorder.GetSupportedVideoResolutions 의 비원자적 캐시 → Lazy<T> 로 thread-safe 화 [Scope: src/Tizen.Multimedia.Recorder] (2026-05-17) #7641

@JoonghyunCho

Description

@JoonghyunCho

[Type: Refactoring]
[Scope: src/Tizen.Multimedia.Recorder]
[Priority: 🟡 Improvement]
[Lens: Coding Guidelines, Performance]

Observation

VideoRecorder.Capabilities.cs:74-91GetSupportedVideoResolutions(CameraDevice) 는 정적 캐시 _frontResolutions / _rearResolutions??= 와 등가인 인라인 할당 으로 채웁니다. 락이 없어 동시에 두 스레드가 진입하면 LoadVideoResolutions 가 중복 호출되고, 그 안에서 new Camera(device) (네이티브 핸들 + 권한) 가 함께 두 번 생성됩니다.

Before

// src/Tizen.Multimedia.Recorder/Recorder/VideoRecorder.Capabilities.cs:74-91
private static IEnumerable<Size> _frontResolutions;
private static IEnumerable<Size> _rearResolutions;

public static IEnumerable<Size> GetSupportedVideoResolutions(CameraDevice device)
{
    ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));

    if (device == CameraDevice.Front)
    {
        return _frontResolutions ?? (_frontResolutions = LoadVideoResolutions(CameraDevice.Front));
    }

    if (device == CameraDevice.Rear)
    {
        return _rearResolutions ?? (_rearResolutions = LoadVideoResolutions(CameraDevice.Rear));
    }

    Debug.Fail("No cache for " + device + ".");
    return LoadVideoResolutions(device);
}

Problem

  • 📐 Coding Guidelines (Thread Safety) — read → null check → assignment 이 비원자적이라 두 스레드가 동시에 진입하면 LoadVideoResolutions중복 실행 됩니다. 그 안에서:
    • new Camera(device) ⇒ 네이티브 카메라 핸들 두 개 동시 오픈 시도
    • Tizen 카메라 권한/리소스가 단일 점유형이라 두 번째 호출이 NotSupportedException / InvalidOperationException 으로 실패할 수 있음
    • 첫 호출의 반환값이 캐시에 저장되지 않고 버려질 수 있음 (lost write)
  • 🚀 Performance — 카메라 오픈 → 해상도 열거 → 카메라 닫기 사이클은 수십~수백 ms. 캐시가 무의미해지면 매 호출 비용 발생.

Proposed Improvement

Lazy<T> 로 변경 — 표준 thread-safe 캐시 패턴이며 동일 디렉터리 내 Recorder.Capabilities.cs 가 이미 같은 패턴 (Lazy<Capabilities>) 을 사용하고 있어 코드베이스 컨벤션에도 부합합니다.

After

private static readonly Lazy<IEnumerable<Size>> _frontResolutions =
    new(() => LoadVideoResolutions(CameraDevice.Front));

private static readonly Lazy<IEnumerable<Size>> _rearResolutions =
    new(() => LoadVideoResolutions(CameraDevice.Rear));

public static IEnumerable<Size> GetSupportedVideoResolutions(CameraDevice device)
{
    ValidationUtil.ValidateEnum(typeof(CameraDevice), device, nameof(device));

    return device switch
    {
        CameraDevice.Front => _frontResolutions.Value,
        CameraDevice.Rear  => _rearResolutions.Value,
        _ => LoadVideoResolutions(device), // unreachable post-ValidateEnum, kept for safety
    };
}

Lazy<T> 의 기본값 LazyThreadSafetyMode.ExecutionAndPublication 이 동시 진입 시 한 번만 실행되도록 보장합니다.

Target Files

  • src/Tizen.Multimedia.Recorder/Recorder/VideoRecorder.Capabilities.cs (L29-L30, L74-L91)

Expected Impact (Quantitative Metrics)

  • 중복 LoadVideoResolutions 호출: 가능 → 불가능 (정확성 픽스)
  • 중복 new Camera(device) 네이티브 핸들 오픈: 가능 → 불가능 (자원 안전성 픽스)
  • 데이터 레이스 윈도우: O(1) μs → 0
  • 추가 할당: Lazy<T> 인스턴스 2개, 클래스 로드 시 1회 (약 64 바이트 × 2)
  • 코드 라인 증가: 0 (오히려 3 라인 감소 + switch expression 가독성 ↑)
  • C# 12 switch expression 모더나이제이션 부가 효과

API Compatibility Check

  • Public API signature change: 없음
  • Behavior change: 없음 (성공 경로 결과는 동일; 동시 호출 시에만 정확성이 회복됨)
  • Tizen API Level floor: 유지 (Lazy<T> 는 .NET Framework 4.0+ 표준)

Impact Scope

  • 호출 사이트: 1 메서드 (GetSupportedVideoResolutions 자체) — public API 단일 진입점
  • 외부 어셈블리 영향 없음 (구현 세부)
  • 동일 디렉터리의 Recorder.Capabilities.cs:Capabilities = new Lazy<...>(...) 패턴과 일관화

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions