atomic -- 아토믹
atomic 모듈은 C11 <stdatomic.h>를 감싸, 공유 정수에 대한 락 프리 아토믹 연산을 제공한다. 모든 연산은 순차 일관성(sequential consistency) 순서를 사용한다.
c
#include "atomic.h"atomic_int (32비트 부호 있음)
로드 / 스토어
c
int atomic_load_int(const int* addr);
void atomic_store_int(int* addr, int val);*addr를 원자적으로 읽거나 쓴다.
페치 후 변경 (Fetch-and-Modify)
c
int atomic_fetch_add_int(int* addr, int delta);
int atomic_fetch_sub_int(int* addr, int delta);
int atomic_fetch_and_int(int* addr, int mask);
int atomic_fetch_or_int(int* addr, int mask);
int atomic_fetch_xor_int(int* addr, int mask);각 함수는 연산을 원자적으로 적용하고 이전 값을 반환한다.
교환 (Exchange)
c
int atomic_exchange_int(int* addr, int val);*addr를 val로 원자적으로 교체하고 이전 값을 반환한다.
Compare-and-Swap (CAS)
c
int atomic_cas_int(int* addr, int* expected, int desired);*addr == *expected이면 desired를 *addr에 원자적으로 저장하고 1(성공)을 반환한다. 그렇지 않으면 *expected를 *addr의 현재 값으로 갱신하고 0(실패)을 반환한다.
atomic_long (64비트 부호 있음)
c
long long atomic_load_ll(const long long* addr);
void atomic_store_ll(long long* addr, long long val);
long long atomic_fetch_add_ll(long long* addr, long long delta);
long long atomic_fetch_sub_ll(long long* addr, long long delta);
long long atomic_exchange_ll(long long* addr, long long val);
int atomic_cas_ll(long long* addr, long long* expected, long long desired);_int 변형과 동일한 의미를 가지며, long long(64비트) 값에 대해 동작한다.
메모리 펜스
c
void atomic_thread_fence();완전한 순차 일관성 메모리 펜스를 발행한다. 이는 펜스 이전의 모든 메모리 연산이, 펜스 이후의 어떤 메모리 연산보다 먼저 모든 스레드에 보이도록 보장한다.
예제: 락 프리 카운터
c
#include "atomic.h"
#include "thread.h"
#include "io.h"
int counter = 0;
void* increment(void* arg) {
int i = 0;
while (i < 10000) {
atomic_fetch_add_int(&counter, 1);
i = i + 1;
}
return 0;
}
int main() {
unsigned long long t1;
unsigned long long t2;
thread_create(&t1, increment, 0);
thread_create(&t2, increment, 0);
thread_join(t1);
thread_join(t2);
print("counter = ");
println_int(atomic_load_int(&counter)); // 20000
return 0;
}예제: Compare-and-Swap 루프
c
#include "atomic.h"
#include "io.h"
int main() {
int val = 10;
// 10 -> 20 으로 변경 시도
int expected = 10;
int ok = atomic_cas_int(&val, &expected, 20);
print("CAS succeeded: ");
println_int(ok); // 1
print("val = ");
println_int(val); // 20
// 오래된 expected 값으로 다시 시도
expected = 10;
ok = atomic_cas_int(&val, &expected, 30);
print("CAS succeeded: ");
println_int(ok); // 0
print("expected updated to: ");
println_int(expected); // 20 (현재 값)
return 0;
}안전성 참고 사항
아토믹 연산 자체는 메모리 안전하다. 다만 C FFI로부터 받은 원시 포인터에 결과를 저장하는 것은 SafeC의 FFI 정책에 따라 호출부에서 unsafe {}가 필요하다.