Zig Io.Threaded, POSIX 신호와 공유 플래그로 블로킹 syscall 취소 처리
- Zig의 std.Io.Threaded는 일반 OS 스레드와 블로킹 syscall을 쓰면서도 작업 취소를 지원하는 새 Io 인터페이스 구현체임
- POSIX에서는 취소 요청 스레드가 공유 메모리 플래그를 설정한 뒤 대상 스레드에 신호를 반복 전송하고, 블로킹 syscall이 EINTR로 깨어나면 대상이 플래그를 확인해 재시도하거나 취소를 승인함
- 신호는 syscall 시작 전이나 종료 후에 도착할 수 있고 무관한 신호도 syscall을 중단시킬 수 있으므로, 신호 자체가 아니라 공유 플래그 확인과 승인 응답으로 취소 경쟁 조건을 처리함
- 사용자 코드에는 취소 요청이 error.Canceled로 나타나며, Zig의 reader와 writer 인터페이스는 오류 타입을 지워 I/O 취소를 전달함
- Windows에서는 NtCancelSynchronousIoFile로 동기 I/O 취소를 직접 지원하며, 글은 Java의 thread interruption이 일반 syscall 중단을 지원하지 않는다고 비교함
Hacker News opinions
Rust로 배포 중인데, 이걸 Zig로 해야 할 이유가 있나? AI 보조 개발을 하는 개발자나 최종 사용자에게 체감되는 이점이 뭔지 궁금함.
직접 코드를 안 쓴다면 Rust 쓰는 게 맞다고 봄. AI가 만든 Zig 코드는 컴파일돼도 런타임에서 UB로 터질 수 있는데, Rust의 borrow checker는 그런 버그를 많이 잡아줌.
나는 개인용 오픈소스 도구를 LLM으로 고쳐 쓰는데 Rust 컴파일 시간은 정말 심함. 고사양 머신에서도 Codex CLI 재컴파일에 15분 넘게 걸렸고, Zig 증분 컴파일은 ms 단위라 4자릿수 차이는 충분히 체감됨.
Zig는 내가 원하는 걸 아주 명시적으로 쓰게 함. hello world도 3~4줄인데, 다른 언어가 한 줄로 줄여 주는 건 그만큼 대신 결정해 준다는 뜻임.
Zig 문법은 별로임. 너무 장황하고 기존 문법에서 이상하게 가져온 부분이 많아서, 코드가 시끄럽고 읽기 어렵더라.
나는 문법이 나쁘다기보다 낯설 뿐이라고 봄. |capture|는 오류와 optional 등을 여러 제어문에서 일관되게 받고, blk:로 블록에 이름을 붙이는 방식도 규칙이 있음.
문법은 당연히 중요함. 영어에서 알파벳과 문법이 중요하지 않다고 하는 것과 같은 말임.
내 저수준 개발 경험은 전부 Windows 쪽인데 NT 커널 시절부터 async와 cancel 지원이 있었음. Overlapped I/O면 한 스레드에서도 처리할 수 있고, Linux 쪽이 더 까다로운데 Zig가 이걸 일관된 추상화로 다루는 건 유용해 보임.
Java에는 2000년대 초부터 interruptible channel이 있었음. interrupt()나 close()로 블로킹 I/O를 끊을 수 있음.
글의 prior art 부분을 읽어보는 게 좋겠음. 거기서 Java 인터럽트가 일반 syscall을 끊지 못하는 문제를 구분해서 설명함.