11. Send & Sync — 컴파일 타임 동시성 증명 🟠
학습 목표: Rust의
Send와Sync자동 트레이트(Auto-traits)가 어떻게 컴파일러를 동시성 감사관으로 변모시키는지 배웁니다. 어떤 타입이 스레드 경계를 넘을 수 있는지, 어떤 타입을 안전하게 공유할 수 있는지를 런타임 비용 없이 컴파일 타임에 증명하는 법을 익힙니다.
문제 요망: 안전망 없는 동시 접근
시스템 프로그래밍에서 주변 장치, 공유 버퍼, 전역 상태는 메인 루프, 인터럽트 핸들러, DMA 콜백 등 다양한 컨텍스트에서 접근됩니다. C 언어에서는 volatile 키워드가 최적화를 막아줄 뿐, 데이터 경합(Data race)에 대해서는 아무런 보호도 제공하지 못합니다.
/* C — 인터럽트와 메인 루프 간의 경합 발생 가능 */
volatile uint32_t sensor_buf[64];
volatile uint32_t buf_index = 0;
void SENSOR_IRQHandler(void) {
sensor_buf[buf_index++] = read_sensor(); // 경합: buf_index 읽기 + 쓰기
}
이런 동시성 버그는 재현하기 매우 어렵고, 주로 운영 환경에서 부하가 걸릴 때 간헐적으로 발생합니다.
Send와 Sync가 증명하는 것
Rust는 컴파일러가 자동으로 유도하는 두 가지 마커 트레이트를 정의합니다.
| 트레이트 | 증명 내용 | 비공식적 의미 |
|---|---|---|
Send | T 타입의 값을 다른 스레드로 안전하게 이동할 수 있음 | "스레드 경계를 넘을 수 있음" |
Sync | 공유 참조 &T를 여러 스레드에서 안전하게 사용할 수 있음 | "여러 스레드에서 동시에 읽을 수 있음" |
이들은 **자동 트레이트(Auto-traits)**입니다. 구조체의 모든 필드가 Send이면 구조체도 Send가 됩니다. 하나라도 !Send인 필드가 있다면 전체 구조체도 스레드 간 이동이 불가능해집니다.
주변 장치 핸들의 스레드 격리
하드웨어 레지스터는 특정 메모리 주소에 고정되어 있으며, 원칙적으로 단일 실행 컨텍스트에서만 접근해야 합니다. 원시 포인터(*const T)는 기본적으로 !Send 및 !Sync이므로, 이를 포함하는 핸들은 자동으로 특정 스레드에 갇히게 됩니다.
/// UART 핸들은 원시 포인터를 포함하므로 자동으로 !Send 및 !Sync입니다.
pub struct Uart { regs: *const u32 }
fn main() {
let uart = Uart::new(0x4000_1000);
// ❌ 컴파일 에러: Uart는 !Send이므로 다른 스레드로 보낼 수 없음
// std::thread::spawn(move || { uart.write_byte(b'B'); });
}
Mutex를 통한 Sync 복구
Cell<T>이나 RefCell<T>은 동기화 장치가 없으므로 !Sync입니다. 하지만 여러 스레드에서 데이터를 공유해야 할 때 Mutex<T>로 감싸면 컴파일러는 이를 안전하다고 판단합니다.
T가Send이면,Mutex<T>는Send + Sync입니다.
이는 단순히 동기화하는 것을 넘어, 동기화되었음을 증명하는 것입니다. Mutex::lock()을 통해서만 데이터에 접근할 수 있도록 강제하기 때문에 "잠금(lock)을 잊어버리는 실수"는 구조적으로 불가능해집니다.
핵심 요약
- 컴파일 타임 동시성 감사 —
Send와Sync는 타입의 구조를 분석하여 동시성 안전성을 정적으로 증명합니다. 런타임 비용은 전혀 없습니다. - 원시 포인터와 자동 제외 — 하드웨어 핸들은 기본적으로 스레드에 격리되며, 필요한 경우에만 명시적으로 권한을 부여하게 됩니다.
- 구조적인 잠금 강제 —
Mutex패턴은 잠금 없이 데이터에 접근하는 행위를 컴파일 타임에 원천 차단합니다. - 함수 경계의 정리 —
F: Send + 'static과 같은 트레이트 바운드는 함수 호출자가 스레드 안전성을 증명해야 한다는 '정리(Theorem)'가 됩니다. - 통합된 안전성 — 타입 상태, 팬텀 타입,
const fn증명과 결합하여 프로토콜 순서, 권한, 값의 범위, 그리고 동시성 안전성까지 완벽한 안전망을 구축할 수 있습니다.