UART 드라이버를 다시 읽다가 또 같은 패턴을 봤다. status는 상태 레지스터를 가리키는 volatile uint32_t *이고, 루프는 !(*status & RX_READY)인 동안 돌다가 데이터 레지스터를 읽는다. 여기까지는 문제없다. volatile 덕분에 컴파일러가 그 읽기를 캐시하거나 지우지 못한다.
이제 volatile이 하지 않는 일이다. 주변의 일반 load와 store의 순서를 잡아 주지 않는다. 그래서 전송을 시작하는 volatile 쓰기 직전에 채운 DMA 버퍼가 그 쓰기보다 늦게 기록될 수 있다. 원자적이지도 않다. *ctrl |= TX_EN은 읽기 한 번과 쓰기 한 번이고, 그 사이에 같은 레지스터를 바꾼 인터럽트 핸들러의 변경은 덮어써진다. CPU의 쓰기 버퍼나 캐시에 대해서는 아무 말도 하지 않는다. 게다가 C11 6.7.3은 volatile 객체에 대한 접근이 무엇인지를 구현 정의로 남겨 두었다. 이런 코드에서는 컴파일러 매뉴얼도 명세의 일부다.
장치에 필요한 순서는 아키텍처의 배리어 명령이나 그것을 감싼 벤더 헤더에서 나온다. C11 펜스는 스레드를 위해 만들어졌고, 장치 메모리까지 덮는지는 플랫폼이 정한다.
volatile은 드라이버가 올바라지기 시작하는 지점이다. 끝나는 지점과는 거리가 멀다.