<aside> 🔒
직접 만든 락프리 자료구조(큐·스택·프리리스트)를 리눅스로 옮긴 기록이다. 이 자료구조는 Win32 Interlocked* 위에서 설계·검증된 것이라, 옮기려면 원자연산부터 다시 짜야 할 것처럼 보였다.
결과는 자료구조 코드 무변경이다. #include 한 줄과 어댑터 헤더 하나로 끝났고, 이 문서는 그게 어떻게 가능했는지와 그 과정에서 딱 하나 실측이 필요했던 지점을 다룬다.
</aside>
<aside> 📌
범위 — g++ 13.3 / x86-64 / Ubuntu 24.04(WSL2) 기준. 코드는 형제 저장소 LockFree_Test/LockFree/.
자료구조 자체의 설계는 여기서 다루지 않는다 — 그건 LockFree 자료구조 계열에 있다. 여기는 이식만 본다.
전체 요약은 1. 왜 옮겼나 · 결과.
</aside>
| 파일 | 변경 |
|---|---|
InternalFreeList.h |
#include <windows.h> → #include "LockFreeCompat.h" — 1줄 |
LockFreeQueue.h · LockFreeStack.h · ExternalTlsFreeList.h |
없음. 셋 다 InternalFreeList.h를 타고 들어온다 |
LockFreeCompat.h |
신설 — 어댑터 함수 17개, 타입 별칭 8개, 컴파일러 지시자 4개 |
호출부는 InterlockedCompareExchange128·InterlockedIncrement64 같은 이름을 18곳에서 부른다. 그 18곳을 고치는 대신, 같은 이름·같은 시그니처의 함수를 리눅스용으로 하나씩 만들어 이름이 그대로 통하게 했다.
<aside> 🔑
이렇게 한 이유는 편해서가 아니다. 원본과의 diff가 작아야 이식 중에 생긴 오류를 눈으로 잡을 수 있기 때문이다.
자료구조를 손대면서 옮겼다면, 나중에 문제가 났을 때 "원래 그랬나, 옮기다 깨졌나"를 가릴 수가 없다. 지금은 자료구조 쪽 diff가 한 줄이라 의심할 곳이 어댑터뿐이다.
</aside>
| 갈래 | 개수 | Windows → 리눅스 |
|---|---|---|
| 원자연산 | 5 | Interlocked* → __sync / __atomic (3장에서 자세히) |
| 힙 API | 5 | HeapCreate·HeapAlloc·HeapFree·HeapDestroy·HeapSetInformation → malloc / free |
| 정렬 할당 | 2 | _aligned_malloc / _aligned_free → posix_memalign / free |
| 스레드 지역 저장소 | 4 | TlsAlloc·TlsFree·TlsGetValue·TlsSetValue → pthread_key_* |
| 스레드 양보 | 1 | SwitchToThread → sched_yield |
| 타입 별칭 | 8 | INT64·LONG64·SHORT·PVOID·UINT_PTR·HANDLE·ULONG·DWORD |
| 컴파일러 지시자 | 4 | __forceinline·__declspec·__fastfail·YieldProcessor |
대부분은 이름만 바꿔 끼우면 되는 것들이다. 함정이 셋 있었고, 그중 하나는 실측이 필요했다.
__atomic은 함수 호출을 낸다 ★이 문서에서 유일하게 길게 쓸 대목이다.
이 자료구조는 ABA 문제를 포인터와 카운터를 붙여 한 번에 교환하는 방식으로 푼다. 포인터 8바이트 + 카운터 8바이트 = 16바이트를 원자적으로 바꿔야 하고, x86-64에서 그 명령이 cmpxchg16b다. Windows에서는 InterlockedCompareExchange128이 그 명령으로 내려갔다.
GCC에는 이걸 쓰는 방법이 둘이다.
__atomic_compare_exchange_n — 요즘 방식. C++11 메모리 모델을 그대로 반영한다__sync_val_compare_and_swap — 옛 방식(legacy). GCC 4 시절 것이고, 항상 full barrier다당연히 앞의 것을 골랐다. 그리고 그게 틀렸다.